resource-string-loading-20040401
[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    for (size_t iModule = 0; iModule < l_cModules; ++iModule)
186       {
187       if (l_aModules[ iModule ].wPriority == 0)  // end of list?
188          break;
189       if (l_aModules[ iModule ].hInstance == hInstance)  // found module?
190          break;
191       }
192    if (iModule < l_cModules)
193       {
194       for ( ; iModule < l_cModules-1; ++iModule)
195          memcpy (&l_aModules[ iModule ], &l_aModules[ iModule+1 ], sizeof(MODULE));
196       memset (&l_aModules[ iModule ], 0x00, sizeof(MODULE));
197       }
198
199    // Next, if we have been asked to add this module, find a good place for it.
200    // We want to leave {iModule} pointing to the slot where this module
201    // should be inserted--anything currently at or after {iModule} will have
202    // to be moved outward.
203    //
204    if (wSearchPriority != MODULE_PRIORITY_REMOVE)
205       {
206       for (iModule = 0; iModule < l_cModules; ++iModule)
207          {
208          if (l_aModules[ iModule ].wPriority == 0)  // end of list?
209             break;
210          if (l_aModules[ iModule ].wPriority > wSearchPriority)  // good place?
211             break;
212          }
213
214       for (size_t iTarget = iModule; iTarget < l_cModules; ++iTarget)
215          {
216          if (l_aModules[ iTarget ].wPriority == 0)  // end of list?
217             break;
218          }
219
220       if (REALLOC (l_aModules, l_cModules, 1+iTarget, cREALLOC_MODULES))
221          {
222          for (size_t iSource = iTarget -1; (LONG)iSource >= (LONG)iModule; --iSource, --iTarget)
223             memcpy (&l_aModules[ iTarget ], &l_aModules[ iSource ], sizeof(MODULE));
224
225          l_aModules[ iModule ].wPriority = wSearchPriority;
226          l_aModules[ iModule ].hInstance = hInstance;
227          }
228       }
229
230    LeaveCriticalSection (&l_csModules);
231 }
232
233
234 /*
235  *** FindAfsCommonPath
236  *
237  * Because our localized files are often placed in a single Common directory
238  * for the AFS product, we need to know how to find that directory. This
239  * routine, and its helper FindAfsCommonPathByComponent(), do that search.
240  *
241  */
242
243 BOOL FindAfsCommonPathByComponent (LPTSTR pszCommonPath, LPTSTR pszComponent)
244 {
245    *pszCommonPath = 0;
246
247    TCHAR szRegPath[ MAX_PATH ];
248    wsprintf (szRegPath, TEXT("Software\\TransarcCorporation\\%s\\CurrentVersion"), pszComponent);
249
250    HKEY hk;
251    if (RegOpenKey (HKEY_LOCAL_MACHINE, szRegPath, &hk) == 0)
252       {
253       DWORD dwType = REG_SZ;
254       DWORD dwSize = MAX_PATH;
255
256       if (RegQueryValueEx (hk, TEXT("PathName"), NULL, &dwType, (PBYTE)pszCommonPath, &dwSize) == 0)
257          {
258          *(LPTSTR)FindBaseFileName (pszCommonPath) = TEXT('\0');
259
260          if (pszCommonPath[0] && (pszCommonPath[ lstrlen(pszCommonPath)-1 ] == TEXT('\\')))
261             pszCommonPath[ lstrlen(pszCommonPath)-1 ] = TEXT('\0');
262
263          if (pszCommonPath[0])
264             lstrcat (pszCommonPath, TEXT("\\Common"));
265          }
266
267       RegCloseKey (hk);
268       }
269
270    return !!*pszCommonPath;
271 }
272
273
274 BOOL FindAfsCommonPath (LPTSTR pszCommonPath)
275 {
276    if (FindAfsCommonPathByComponent (pszCommonPath, TEXT("AFS Client")))
277       return TRUE;
278    if (FindAfsCommonPathByComponent (pszCommonPath, TEXT("AFS Control Center")))
279       return TRUE;
280    if (FindAfsCommonPathByComponent (pszCommonPath, TEXT("AFS Server")))
281       return TRUE;
282    if (FindAfsCommonPathByComponent (pszCommonPath, TEXT("AFS Supplemental Documentation")))
283       return TRUE;
284    return FALSE;
285 }
286
287
288 /*
289  *** TaLocale_LoadCorrespondingModule
290  *
291  * This routine looks for a .DLL named after the specified module, but
292  * with a suffix reflecting the current locale--it loads that library
293  * and calls TaLocale_SpecifyModule for the library.
294  *
295  */
296
297 HINSTANCE TaLocale_LoadCorrespondingModule (HINSTANCE hInstance, WORD wSearchPriority)
298 {
299    // If the caller was sloppy and didn't supply an instance handle,
300    // assume we should find the module corresponding with the current .EXE.
301    //
302    if (hInstance == NULL)
303       hInstance = GetModuleHandle(NULL);
304
305    TCHAR szFilename[ MAX_PATH ] = TEXT("");
306    GetModuleFileName (hInstance, szFilename, MAX_PATH);
307
308    return TaLocale_LoadCorrespondingModuleByName (hInstance, szFilename, wSearchPriority);
309 }
310
311 HINSTANCE TaLocale_LoadCorrespondingModuleByName (HINSTANCE hInstance, LPTSTR pszFilename, WORD wSearchPriority)
312 {
313    HINSTANCE hDLL = NULL;
314
315    TCHAR szFilename[ MAX_PATH ];
316    if (lstrchr (pszFilename, TEXT('\\')) != NULL)
317       lstrcpy (szFilename, pszFilename);
318    else
319       {
320       GetModuleFileName (hInstance, szFilename, MAX_PATH);
321       lstrcpy ((LPTSTR)FindBaseFileName (szFilename), pszFilename);
322       }
323
324
325    // If the caller was sloppy and didn't supply an instance handle,
326    // assume we should find the module corresponding with the current .EXE.
327    //
328    if (hInstance == NULL)
329       hInstance = GetModuleHandle(NULL);
330
331    LPTSTR pchExtension;
332    if ((pchExtension = (LPTSTR)FindExtension (szFilename)) != NULL)
333       {
334
335       // Find the filename associated with the specified module, remove its
336       // extension, and append "_409.dll" (where the 409 is, naturally, the
337       // current LANGID). Then try to load that library.
338       //
339       wsprintf (pchExtension, TEXT("_%lu.dll"), TaLocale_GetLanguage());
340       if ((hDLL = LoadLibrary (szFilename)) == NULL)
341          hDLL = LoadLibrary (FindBaseFileName (szFilename));
342
343       // If we couldn't find the thing under that name, it's possible we
344       // have a .DLL made for the proper language but not for the user's
345       // specific sublanguage (say, a US English .DLL but we're running
346       // in a Canadian English locale). Make an intelligent guess about
347       // what the valid ID would be.
348       //
349       if (hDLL == NULL)
350          {
351          wsprintf (pchExtension, TEXT("_%lu.dll"), TaLocale_GuessBestLangID (TaLocale_GetLanguage()));
352          if ((hDLL = LoadLibrary (szFilename)) == NULL)
353             hDLL = LoadLibrary (FindBaseFileName (szFilename));
354          }
355
356       // If we STILL couldn't find a corresponding resource library,
357       // we'll take anything we can find; this should cover the case
358       // where a Setup program asked the user what language to use,
359       // and just installed that matching library. Look in the
360       // appropriate directory for any .DLL that fits the naming convention.
361       //
362       if (hDLL == NULL)
363          {
364          wsprintf (pchExtension, TEXT("_*.dll"));
365
366          WIN32_FIND_DATA wfd;
367          memset (&wfd, 0x00, sizeof(wfd));
368
369          HANDLE hFind;
370          if ((hFind = FindFirstFile (szFilename, &wfd)) != NULL)
371             {
372             if (wfd.cFileName[0])
373                {
374                wsprintf ((LPTSTR)FindBaseFileName (szFilename), wfd.cFileName);
375                hDLL = LoadLibrary (szFilename);
376                }
377             FindClose (hFind);
378             }
379          }
380
381       // If we EVEN NOW couldn't find a corresponding resource library,
382       // we may have done our wildcard search in the wrong directory.
383       // Try to find the Common subdirectory of our AFS installation,
384       // and look for any matching DLL there.
385       //
386       if (hDLL == NULL)
387          {
388          wsprintf (pchExtension, TEXT("_*.dll"));
389
390          TCHAR szCommonPath[ MAX_PATH ];
391          if (FindAfsCommonPath (szCommonPath))
392             {
393             lstrcat (szCommonPath, TEXT("\\"));
394             lstrcat (szCommonPath, FindBaseFileName (szFilename));
395
396             WIN32_FIND_DATA wfd;
397             memset (&wfd, 0x00, sizeof(wfd));
398
399             HANDLE hFind;
400             if ((hFind = FindFirstFile (szCommonPath, &wfd)) != NULL)
401                {
402                if (wfd.cFileName[0])
403                   {
404                   wsprintf ((LPTSTR)FindBaseFileName (szCommonPath), wfd.cFileName);
405                   hDLL = LoadLibrary (szCommonPath);
406                   }
407                FindClose (hFind);
408                }
409             }
410          }
411
412       // If all else fails, we'll try to find the English library
413       // somewhere on our path.
414       //
415       if (hDLL == NULL)
416          {
417          wsprintf (pchExtension, TEXT("_%lu.dll"), MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US));
418          if ((hDLL = LoadLibrary (szFilename)) == NULL)
419             hDLL = LoadLibrary (FindBaseFileName (szFilename));
420          }
421
422       // If we were successful in loading the resource library, add it
423       // to our chain of modules-to-search
424       //
425       if (hDLL != NULL)
426          {
427          TaLocale_SpecifyModule (hDLL, wSearchPriority);
428          }
429       }
430
431    return hDLL;
432 }
433
434
435 /*
436  *** TaLocale_EnumModule
437  *
438  * Enables enumeration of each of the product modules which will be seached
439  * by TaLocale for resources. Use TaLocale_SpecifyModule to modify this list.
440  * Modules will be returned in priority order, with the highest-priority
441  * modules being searched first.
442  *
443  */
444
445 BOOL TaLocale_EnumModule (size_t iModule, HINSTANCE *phInstance, WORD *pwSearchPriority)
446 {
447    BOOL rc = FALSE;
448    TaLocale_Initialize();
449    EnterCriticalSection (&l_csModules);
450
451    if ( (iModule < l_cModules) && (l_aModules[ iModule ].wPriority != 0) )
452       {
453       rc = TRUE;
454       if (phInstance)
455          *phInstance = l_aModules[ iModule ].hInstance;
456       if (pwSearchPriority)
457          *pwSearchPriority = l_aModules[ iModule ].wPriority;
458       }
459
460    LeaveCriticalSection (&l_csModules);
461    return rc;
462 }
463
464
465 /*
466  *** TaLocale_GetLanguage
467  *** TaLocale_SetLanguage
468  *
469  * Allows specification of a default language for resources extracted by
470  * the functions exported by tal_string.h and tal_dialog.h. When a particular
471  * string or dialog resource is required, the resource which matches this
472  * specified language will be retrieved--if no localized resource is available,
473  * the default resource will instead be used.
474  *
475  */
476
477 LANGID TaLocale_GetLanguage (void)
478 {
479    TaLocale_Initialize();
480    return l_lang;
481 }
482
483
484 void TaLocale_SetLanguage (LANGID lang)
485 {
486    TaLocale_Initialize();
487    l_lang = lang;
488 }
489
490
491 /*
492  *** TaLocale_GetLanguageOverride
493  *** TaLocale_SetLanguageOverride
494  *
495  * Allows specification of a persistent default language for resources
496  * extracted by the functions exported by tal_string.h and tal_dialog.h.
497  * If a language override (which is really just a registry entry) exists,
498  * all TaLocale-based applications will default to that locale when first
499  * run.
500  *
501  */
502
503 static HKEY REGSTR_BASE_OVERRIDE = HKEY_LOCAL_MACHINE;
504 static TCHAR REGSTR_PATH_OVERRIDE[] = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Nls");
505 static TCHAR REGSTR_VAL_OVERRIDE[] = TEXT("Default Language");
506
507 LANGID TaLocale_GetLanguageOverride (void)
508 {
509    DWORD dwLang = 0;
510
511    HKEY hk;
512    if (RegOpenKey (REGSTR_BASE_OVERRIDE, REGSTR_PATH_OVERRIDE, &hk) == 0)
513       {
514       DWORD dwType = REG_DWORD;
515       DWORD dwSize = sizeof(DWORD);
516       if (RegQueryValueEx (hk, REGSTR_VAL_OVERRIDE, 0, &dwType, (PBYTE)&dwLang, &dwSize) != 0)
517          dwLang = 0;
518       RegCloseKey (hk);
519       }
520
521    return (LANGID)dwLang;
522 }
523
524
525 void TaLocale_SetLanguageOverride (LANGID lang)
526 {
527    HKEY hk;
528    if (RegCreateKey (REGSTR_BASE_OVERRIDE, REGSTR_PATH_OVERRIDE, &hk) == 0)
529       {
530       DWORD dwLang = (DWORD)lang;
531       RegSetValueEx (hk, REGSTR_VAL_OVERRIDE, 0, REG_DWORD, (PBYTE)&dwLang, sizeof(DWORD));
532       RegCloseKey (hk);
533       }
534 }
535
536
537 void TaLocale_RemoveLanguageOverride (void)
538 {
539    HKEY hk;
540    if (RegOpenKey (REGSTR_BASE_OVERRIDE, REGSTR_PATH_OVERRIDE, &hk) == 0)
541       {
542       RegDeleteValue (hk, REGSTR_VAL_OVERRIDE);
543       RegCloseKey (hk);
544       }
545 }
546
547
548 /*
549  *** TaLocale_GetResource
550  *
551  * Returns a pointer an in-memory image of a language-specific resource.
552  * The resource is found by searching all specified module handles
553  * (as determined automatically, or as specified by previous calls to
554  * TaLocale_SpecifyModule()) for an equivalent resource identifier matching
555  * the requested resource type and localized into the requested language.
556  * In the event that a matching localized resource cannot be found, the
557  * search is repeated using LANG_USER_DEFAULT.
558  *
559  * The pointer returned should be treated as read-only, and should not be freed.
560  *
561  */
562
563 LPCVOID TaLocale_GetResourceEx (LPCTSTR pszType, LPCTSTR pszRes, LANGID lang, HINSTANCE *phInstFound, BOOL fSearchDefaultLanguageToo)
564 {
565    PVOID pr = NULL;
566
567    HINSTANCE hInstance;
568    for (size_t iModule = 0; TaLocale_EnumModule (iModule, &hInstance); ++iModule)
569       {
570       HRSRC hr;
571       if ((hr = FindResourceEx (hInstance, pszType, pszRes, lang)) == NULL)
572          {
573          // Our translation teams don't usually change the language
574          // constants within .RC files, so we should look for English
575          // language translations too.
576          //
577          if ((hr = FindResourceEx (hInstance, pszType, pszRes, MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US))) == NULL)
578             {
579             // If we still can't find it, we'll take anything...
580             //
581             if ((hr = FindResource (hInstance, pszType, pszRes)) == NULL)
582                continue;
583             }
584          }
585
586       // Once we get here, we'll only fail for weird out-of-memory problems.
587       //
588       HGLOBAL hg;
589       if ((hg = LoadResource (hInstance, hr)) != NULL)
590          {
591          if ((pr = (PVOID)LockResource (hg)) != NULL)
592             {
593             if (phInstFound)
594                *phInstFound = hInstance;
595             break;
596             }
597          }
598       }
599
600    return pr;
601 }
602
603
604 LPCVOID TaLocale_GetResource (LPCTSTR pszType, LPCTSTR pszRes, LANGID lang, HINSTANCE *phInstFound)
605 {
606    return TaLocale_GetResourceEx (pszType, pszRes, lang, phInstFound, TRUE);
607 }
608
609
610
611 /*
612  *** TaLocale_GetStringResource
613  *** TaLocale_GetDialogResource
614  *
615  * Convenience wrappers around TaLocale_GetResource for obtaining resources
616  * of a particular type by numeric identifier; these routines specify the
617  * default language (from TaLocale_SetLanguage()) when calling
618  * TaLocale_GetResource().
619  *
620  */
621
622 LPCDLGTEMPLATE TaLocale_GetDialogResource (int idd, HINSTANCE *phInstFound)
623 {
624    return (LPCDLGTEMPLATE)TaLocale_GetResource (RT_DIALOG, MAKEINTRESOURCE( idd ), TaLocale_GetLanguage(), phInstFound);
625 }
626
627
628 LPCSTRINGTEMPLATE TaLocale_GetStringResource (int ids, HINSTANCE *phInstFound)
629 {
630     // Strings are organized into heaps of String Tables, each table
631     // holding 16 strings (regardless of their length). The first table's
632     // first string index is for string #1. When searching for a string,
633     // the string's table is the index given to FindResource.
634     //
635     LPCSTRINGTEMPLATE pst = NULL;
636     LANGID lang = TaLocale_GetLanguage();
637
638     int iTable = (ids / 16) + 1;           // 1 = first string table
639     int iIndex = ids - ((iTable-1) * 16);  // 0 = first string in the table
640
641     HINSTANCE hInstance;
642     for (size_t iModule = 0; !pst && TaLocale_EnumModule (iModule, &hInstance); ++iModule)
643     {
644         HRSRC hr;
645         if ((hr = FindResourceEx (hInstance, RT_STRING, MAKEINTRESOURCE( iTable ), lang)) == NULL)
646         {
647             // Our translation teams don't usually change the language
648             // constants within .RC files, so we should look for English
649             // language translations too.
650             //
651             if ((hr = FindResourceEx (hInstance, RT_STRING, MAKEINTRESOURCE( iTable ), MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US))) == NULL)
652             {
653                 // If we still can't find it, we'll take anything...
654                 //
655                 if ((hr = FindResource (hInstance, RT_STRING, MAKEINTRESOURCE( iTable ))) == NULL)
656                     continue;
657             }
658         }
659
660         HGLOBAL hg;
661         if ((hg = LoadResource (hInstance, hr)) != NULL)
662         {
663             const WORD *pTable;
664             if     ((pTable = (WORD*)LockResource (hg)) != NULL)
665             {
666                 try {
667                     // Skip words in the string table until we reach the string
668                     // index we're looking for.
669                     //
670                     for (int iIndexWalk = iIndex; iIndexWalk && ((LPCSTRINGTEMPLATE)pTable)->cchString; --iIndexWalk) {
671                         pTable += 1 + ((LPCSTRINGTEMPLATE)pTable)->cchString;
672                     }
673
674                     if (IsValidStringTemplate ((LPCSTRINGTEMPLATE)pTable))
675                     {
676                         pst = (LPCSTRINGTEMPLATE)pTable;
677                         if (phInstFound)
678                             *phInstFound = hInstance;
679                     } else {
680                         UnlockResource(pTable);
681                         FreeResource(hg);
682                     }
683                 }
684                 catch(...)
685                 {
686                     UnlockResource(pTable);
687                     FreeResource(hg);
688                     // If we walked off the end of the table, then the
689                     // string we want just wasn't there.
690                 }
691             }
692         }
693     }
694
695     return pst;
696 }
697
698
699 BOOL IsValidStringTemplate (LPCSTRINGTEMPLATE pTable)
700 {
701     if (!pTable->cchString)
702         return FALSE;
703
704     for (size_t ii = 0; ii < pTable->cchString; ++ii)
705     {
706         if (!pTable->achString[ii])
707             return FALSE;
708     }
709
710     return TRUE;
711 }
712
713
714 /*
715  *** TaLocale_LoadMenu
716  *** TaLocale_LoadImage
717  *
718  * Replacements for Win32 functions. By using these functions instead, the
719  * caller can load the appropriate resources regardless of the module in
720  * which they reside, or the language which is required.
721  *
722  */
723
724 HMENU TaLocale_LoadMenu (int idm)
725 {
726    const MENUTEMPLATE *pTemplate;
727    if ((pTemplate = (const MENUTEMPLATE *)TaLocale_GetResource (RT_MENU, MAKEINTRESOURCE( idm ), TaLocale_GetLanguage())) == NULL)
728       return NULL;
729    return LoadMenuIndirect (pTemplate);
730 }
731
732
733 HANDLE TaLocale_LoadImage (int idi, UINT imageType, int cx, int cy, UINT imageFlags)
734 {
735    HINSTANCE hInstance;
736    for (size_t iModule = 0; TaLocale_EnumModule (iModule, &hInstance); ++iModule)
737       {
738       HANDLE hImage;
739       if (imageType == IMAGE_ICON)
740          hImage = (HANDLE)LoadIcon (hInstance, MAKEINTRESOURCE(idi));
741       else
742          hImage = LoadImage (hInstance, MAKEINTRESOURCE(idi), imageType, cx, cy, imageFlags);
743
744       if (hImage != NULL)
745          return hImage;
746       }
747
748    return NULL;
749 }
750
751
752 HICON TaLocale_LoadIcon (int idi)
753 {
754    return (HICON)TaLocale_LoadImage (idi, IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR);
755 }
756
757
758 HACCEL TaLocale_LoadAccelerators (int ida)
759 {
760    HINSTANCE hInstance;
761    for (size_t iModule = 0; TaLocale_EnumModule (iModule, &hInstance); ++iModule)
762       {
763       HACCEL hAccel;
764       if ((hAccel = LoadAccelerators (hInstance, MAKEINTRESOURCE(ida))) != NULL)
765          return hAccel;
766       }
767
768    return NULL;
769 }
770
771
772