aboutsummaryrefslogtreecommitdiff
path: root/src/dutil/locutil.cpp
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2017-09-03 11:22:38 -0700
committerRob Mensching <rob@firegiant.com>2017-09-03 13:33:33 -0700
commit5d8375007754101ff2889d0e79486c8f9b7cf5ab (patch)
treea76d6fb6a38dd9f04a93ffcfd9d64e76779b3414 /src/dutil/locutil.cpp
parent8e8da6dbc051ec884b5d439bb4f44dc027d05bbf (diff)
downloadwix-5d8375007754101ff2889d0e79486c8f9b7cf5ab.tar.gz
wix-5d8375007754101ff2889d0e79486c8f9b7cf5ab.tar.bz2
wix-5d8375007754101ff2889d0e79486c8f9b7cf5ab.zip
Initial commit
Diffstat (limited to 'src/dutil/locutil.cpp')
-rw-r--r--src/dutil/locutil.cpp613
1 files changed, 613 insertions, 0 deletions
diff --git a/src/dutil/locutil.cpp b/src/dutil/locutil.cpp
new file mode 100644
index 00000000..b3cc042c
--- /dev/null
+++ b/src/dutil/locutil.cpp
@@ -0,0 +1,613 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5// prototypes
6static HRESULT ParseWxl(
7 __in IXMLDOMDocument* pixd,
8 __out WIX_LOCALIZATION** ppWixLoc
9 );
10static HRESULT ParseWxlStrings(
11 __in IXMLDOMElement* pElement,
12 __in WIX_LOCALIZATION* pWixLoc
13 );
14static HRESULT ParseWxlControls(
15 __in IXMLDOMElement* pElement,
16 __in WIX_LOCALIZATION* pWixLoc
17 );
18static HRESULT ParseWxlString(
19 __in IXMLDOMNode* pixn,
20 __in DWORD dwIdx,
21 __in WIX_LOCALIZATION* pWixLoc
22 );
23static HRESULT ParseWxlControl(
24 __in IXMLDOMNode* pixn,
25 __in DWORD dwIdx,
26 __in WIX_LOCALIZATION* pWixLoc
27 );
28
29// from Winnls.h
30#ifndef MUI_LANGUAGE_ID
31#define MUI_LANGUAGE_ID 0x4 // Use traditional language ID convention
32#endif
33#ifndef MUI_MERGE_USER_FALLBACK
34#define MUI_MERGE_USER_FALLBACK 0x20 // GetThreadPreferredUILanguages merges in user preferred languages
35#endif
36#ifndef MUI_MERGE_SYSTEM_FALLBACK
37#define MUI_MERGE_SYSTEM_FALLBACK 0x10 // GetThreadPreferredUILanguages merges in parent and base languages
38#endif
39typedef WINBASEAPI BOOL (WINAPI *GET_THREAD_PREFERRED_UI_LANGUAGES) (
40 __in DWORD dwFlags,
41 __out PULONG pulNumLanguages,
42 __out_ecount_opt(*pcchLanguagesBuffer) PZZWSTR pwszLanguagesBuffer,
43 __inout PULONG pcchLanguagesBuffer
44);
45
46extern "C" HRESULT DAPI LocProbeForFile(
47 __in_z LPCWSTR wzBasePath,
48 __in_z LPCWSTR wzLocFileName,
49 __in_z_opt LPCWSTR wzLanguage,
50 __inout LPWSTR* psczPath
51 )
52{
53 HRESULT hr = S_OK;
54 LPWSTR sczProbePath = NULL;
55 LANGID langid = 0;
56 LPWSTR sczLangIdFile = NULL;
57 LPWSTR sczLangsBuff = NULL;
58 GET_THREAD_PREFERRED_UI_LANGUAGES pvfnGetThreadPreferredUILanguages =
59 reinterpret_cast<GET_THREAD_PREFERRED_UI_LANGUAGES>(
60 GetProcAddress(GetModuleHandle("Kernel32.dll"), "GetThreadPreferredUILanguages"));
61
62 // If a language was specified, look for a loc file in that as a directory.
63 if (wzLanguage && *wzLanguage)
64 {
65 hr = PathConcat(wzBasePath, wzLanguage, &sczProbePath);
66 ExitOnFailure(hr, "Failed to concat base path to language.");
67
68 hr = PathConcat(sczProbePath, wzLocFileName, &sczProbePath);
69 ExitOnFailure(hr, "Failed to concat loc file name to probe path.");
70
71 if (FileExistsEx(sczProbePath, NULL))
72 {
73 ExitFunction();
74 }
75 }
76
77 if (pvfnGetThreadPreferredUILanguages)
78 {
79 ULONG nLangs;
80 ULONG cchLangs = 0;
81 DWORD dwFlags = MUI_LANGUAGE_ID | MUI_MERGE_USER_FALLBACK | MUI_MERGE_SYSTEM_FALLBACK;
82 if (!(*pvfnGetThreadPreferredUILanguages)(dwFlags, &nLangs, NULL, &cchLangs))
83 {
84 ExitWithLastError(hr, "GetThreadPreferredUILanguages failed to return buffer size.");
85 }
86
87 hr = StrAlloc(&sczLangsBuff, cchLangs);
88 ExitOnFailure(hr, "Failed to allocate buffer for languages");
89
90 nLangs = 0;
91 if (!(*pvfnGetThreadPreferredUILanguages)(dwFlags, &nLangs, sczLangsBuff, &cchLangs))
92 {
93 ExitWithLastError(hr, "GetThreadPreferredUILanguages failed to return language list.");
94 }
95
96 LPWSTR szLangs = sczLangsBuff;
97 for (ULONG i = 0; i < nLangs; ++i, szLangs += 5)
98 {
99 // StrHexDecode assumes low byte is first. We'll need to swap the bytes once we parse out the value.
100 hr = StrHexDecode(szLangs, reinterpret_cast<BYTE*>(&langid), sizeof(langid));
101 ExitOnFailure(hr, "Failed to parse langId.");
102
103 langid = MAKEWORD(HIBYTE(langid), LOBYTE(langid));
104 hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName);
105 ExitOnFailure(hr, "Failed to format user preferred langid.");
106
107 hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath);
108 ExitOnFailure(hr, "Failed to concat user preferred langid file name to base path.");
109
110 if (FileExistsEx(sczProbePath, NULL))
111 {
112 ExitFunction();
113 }
114 }
115 }
116
117 langid = ::GetUserDefaultUILanguage();
118
119 hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName);
120 ExitOnFailure(hr, "Failed to format user langid.");
121
122 hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath);
123 ExitOnFailure(hr, "Failed to concat user langid file name to base path.");
124
125 if (FileExistsEx(sczProbePath, NULL))
126 {
127 ExitFunction();
128 }
129
130 if (MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT) != langid)
131 {
132 langid = MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT);
133
134 hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName);
135 ExitOnFailure(hr, "Failed to format user langid (default sublang).");
136
137 hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath);
138 ExitOnFailure(hr, "Failed to concat user langid file name to base path (default sublang).");
139
140 if (FileExistsEx(sczProbePath, NULL))
141 {
142 ExitFunction();
143 }
144 }
145
146 langid = ::GetSystemDefaultUILanguage();
147
148 hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName);
149 ExitOnFailure(hr, "Failed to format system langid.");
150
151 hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath);
152 ExitOnFailure(hr, "Failed to concat system langid file name to base path.");
153
154 if (FileExistsEx(sczProbePath, NULL))
155 {
156 ExitFunction();
157 }
158
159 if (MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT) != langid)
160 {
161 langid = MAKELANGID(langid & 0x3FF, SUBLANG_DEFAULT);
162
163 hr = StrAllocFormatted(&sczLangIdFile, L"%u\\%ls", langid, wzLocFileName);
164 ExitOnFailure(hr, "Failed to format user langid (default sublang).");
165
166 hr = PathConcat(wzBasePath, sczLangIdFile, &sczProbePath);
167 ExitOnFailure(hr, "Failed to concat user langid file name to base path (default sublang).");
168
169 if (FileExistsEx(sczProbePath, NULL))
170 {
171 ExitFunction();
172 }
173 }
174
175 // Finally, look for the loc file in the base path.
176 hr = PathConcat(wzBasePath, wzLocFileName, &sczProbePath);
177 ExitOnFailure(hr, "Failed to concat loc file name to base path.");
178
179 if (!FileExistsEx(sczProbePath, NULL))
180 {
181 hr = E_FILENOTFOUND;
182 }
183
184LExit:
185 if (SUCCEEDED(hr))
186 {
187 hr = StrAllocString(psczPath, sczProbePath, 0);
188 }
189
190 ReleaseStr(sczLangIdFile);
191 ReleaseStr(sczProbePath);
192 ReleaseStr(sczLangsBuff);
193
194 return hr;
195}
196
197extern "C" HRESULT DAPI LocLoadFromFile(
198 __in_z LPCWSTR wzWxlFile,
199 __out WIX_LOCALIZATION** ppWixLoc
200 )
201{
202 HRESULT hr = S_OK;
203 IXMLDOMDocument* pixd = NULL;
204
205 hr = XmlLoadDocumentFromFile(wzWxlFile, &pixd);
206 ExitOnFailure(hr, "Failed to load WXL file as XML document.");
207
208 hr = ParseWxl(pixd, ppWixLoc);
209 ExitOnFailure(hr, "Failed to parse WXL.");
210
211LExit:
212 ReleaseObject(pixd);
213
214 return hr;
215}
216
217extern "C" HRESULT DAPI LocLoadFromResource(
218 __in HMODULE hModule,
219 __in_z LPCSTR szResource,
220 __out WIX_LOCALIZATION** ppWixLoc
221 )
222{
223 HRESULT hr = S_OK;
224 LPVOID pvResource = NULL;
225 DWORD cbResource = 0;
226 LPWSTR sczXml = NULL;
227 IXMLDOMDocument* pixd = NULL;
228
229 hr = ResReadData(hModule, szResource, &pvResource, &cbResource);
230 ExitOnFailure(hr, "Failed to read theme from resource.");
231
232 hr = StrAllocStringAnsi(&sczXml, reinterpret_cast<LPCSTR>(pvResource), cbResource, CP_UTF8);
233 ExitOnFailure(hr, "Failed to convert XML document data from UTF-8 to unicode string.");
234
235 hr = XmlLoadDocument(sczXml, &pixd);
236 ExitOnFailure(hr, "Failed to load theme resource as XML document.");
237
238 hr = ParseWxl(pixd, ppWixLoc);
239 ExitOnFailure(hr, "Failed to parse WXL.");
240
241LExit:
242 ReleaseObject(pixd);
243 ReleaseStr(sczXml);
244
245 return hr;
246}
247
248extern "C" void DAPI LocFree(
249 __in_opt WIX_LOCALIZATION* pWixLoc
250 )
251{
252 if (pWixLoc)
253 {
254 for (DWORD idx = 0; idx < pWixLoc->cLocStrings; ++idx)
255 {
256 ReleaseStr(pWixLoc->rgLocStrings[idx].wzId);
257 ReleaseStr(pWixLoc->rgLocStrings[idx].wzText);
258 }
259
260 for (DWORD idx = 0; idx < pWixLoc->cLocControls; ++idx)
261 {
262 ReleaseStr(pWixLoc->rgLocControls[idx].wzControl);
263 ReleaseStr(pWixLoc->rgLocControls[idx].wzText);
264 }
265
266 ReleaseMem(pWixLoc->rgLocStrings);
267 ReleaseMem(pWixLoc->rgLocControls);
268 ReleaseMem(pWixLoc);
269 }
270}
271
272extern "C" HRESULT DAPI LocLocalizeString(
273 __in const WIX_LOCALIZATION* pWixLoc,
274 __inout LPWSTR* ppsczInput
275 )
276{
277 Assert(ppsczInput && pWixLoc);
278 HRESULT hr = S_OK;
279
280 for (DWORD i = 0; i < pWixLoc->cLocStrings; ++i)
281 {
282 hr = StrReplaceStringAll(ppsczInput, pWixLoc->rgLocStrings[i].wzId, pWixLoc->rgLocStrings[i].wzText);
283 ExitOnFailure(hr, "Localizing string failed.");
284 }
285
286LExit:
287 return hr;
288}
289
290extern "C" HRESULT DAPI LocGetControl(
291 __in const WIX_LOCALIZATION* pWixLoc,
292 __in_z LPCWSTR wzId,
293 __out LOC_CONTROL** ppLocControl
294 )
295{
296 HRESULT hr = S_OK;
297 LOC_CONTROL* pLocControl = NULL;
298
299 for (DWORD i = 0; i < pWixLoc->cLocControls; ++i)
300 {
301 pLocControl = &pWixLoc->rgLocControls[i];
302
303 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pLocControl->wzControl, -1, wzId, -1))
304 {
305 *ppLocControl = pLocControl;
306 ExitFunction1(hr = S_OK);
307 }
308 }
309
310 hr = E_NOTFOUND;
311
312LExit:
313 return hr;
314}
315
316extern "C" HRESULT DAPI LocGetString(
317 __in const WIX_LOCALIZATION* pWixLoc,
318 __in_z LPCWSTR wzId,
319 __out LOC_STRING** ppLocString
320 )
321{
322 HRESULT hr = E_NOTFOUND;
323 LOC_STRING* pLocString = NULL;
324
325 for (DWORD i = 0; i < pWixLoc->cLocStrings; ++i)
326 {
327 pLocString = pWixLoc->rgLocStrings + i;
328
329 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pLocString->wzId, -1, wzId, -1))
330 {
331 *ppLocString = pLocString;
332 hr = S_OK;
333 break;
334 }
335 }
336
337 return hr;
338}
339
340extern "C" HRESULT DAPI LocAddString(
341 __in WIX_LOCALIZATION* pWixLoc,
342 __in_z LPCWSTR wzId,
343 __in_z LPCWSTR wzLocString,
344 __in BOOL bOverridable
345 )
346{
347 HRESULT hr = S_OK;
348
349 ++pWixLoc->cLocStrings;
350 pWixLoc->rgLocStrings = static_cast<LOC_STRING*>(MemReAlloc(pWixLoc->rgLocStrings, sizeof(LOC_STRING) * pWixLoc->cLocStrings, TRUE));
351 ExitOnNull(pWixLoc->rgLocStrings, hr, E_OUTOFMEMORY, "Failed to reallocate memory for localization strings.");
352
353 LOC_STRING* pLocString = pWixLoc->rgLocStrings + (pWixLoc->cLocStrings - 1);
354
355 hr = StrAllocFormatted(&pLocString->wzId, L"#(loc.%s)", wzId);
356 ExitOnFailure(hr, "Failed to set localization string Id.");
357
358 hr = StrAllocString(&pLocString->wzText, wzLocString, 0);
359 ExitOnFailure(hr, "Failed to set localization string Text.");
360
361 pLocString->bOverridable = bOverridable;
362
363LExit:
364 return hr;
365}
366
367// helper functions
368
369static HRESULT ParseWxl(
370 __in IXMLDOMDocument* pixd,
371 __out WIX_LOCALIZATION** ppWixLoc
372 )
373{
374 HRESULT hr = S_OK;
375 IXMLDOMElement *pWxlElement = NULL;
376 WIX_LOCALIZATION* pWixLoc = NULL;
377
378 pWixLoc = static_cast<WIX_LOCALIZATION*>(MemAlloc(sizeof(WIX_LOCALIZATION), TRUE));
379 ExitOnNull(pWixLoc, hr, E_OUTOFMEMORY, "Failed to allocate memory for Wxl file.");
380
381 // read the WixLocalization tag
382 hr = pixd->get_documentElement(&pWxlElement);
383 ExitOnFailure(hr, "Failed to get localization element.");
384
385 // get the Language attribute if present
386 pWixLoc->dwLangId = WIX_LOCALIZATION_LANGUAGE_NOT_SET;
387 hr = XmlGetAttributeNumber(pWxlElement, L"Language", &pWixLoc->dwLangId);
388 if (S_FALSE == hr)
389 {
390 hr = S_OK;
391 }
392 ExitOnFailure(hr, "Failed to get Language value.");
393
394 // store the strings and controls in a node list
395 hr = ParseWxlStrings(pWxlElement, pWixLoc);
396 ExitOnFailure(hr, "Parsing localization strings failed.");
397
398 hr = ParseWxlControls(pWxlElement, pWixLoc);
399 ExitOnFailure(hr, "Parsing localization controls failed.");
400
401 *ppWixLoc = pWixLoc;
402 pWixLoc = NULL;
403
404LExit:
405 ReleaseObject(pWxlElement);
406 ReleaseMem(pWixLoc);
407
408 return hr;
409}
410
411
412static HRESULT ParseWxlStrings(
413 __in IXMLDOMElement* pElement,
414 __in WIX_LOCALIZATION* pWixLoc
415 )
416{
417 HRESULT hr = S_OK;
418 IXMLDOMNode* pixn = NULL;
419 IXMLDOMNodeList* pixnl = NULL;
420 DWORD dwIdx = 0;
421
422 hr = XmlSelectNodes(pElement, L"String", &pixnl);
423 ExitOnLastError(hr, "Failed to get String child nodes of Wxl File.");
424
425 hr = pixnl->get_length(reinterpret_cast<long*>(&pWixLoc->cLocStrings));
426 ExitOnLastError(hr, "Failed to get number of String child nodes in Wxl File.");
427
428 if (0 < pWixLoc->cLocStrings)
429 {
430 pWixLoc->rgLocStrings = static_cast<LOC_STRING*>(MemAlloc(sizeof(LOC_STRING) * pWixLoc->cLocStrings, TRUE));
431 ExitOnNull(pWixLoc->rgLocStrings, hr, E_OUTOFMEMORY, "Failed to allocate memory for localization strings.");
432
433 while (S_OK == (hr = XmlNextElement(pixnl, &pixn, NULL)))
434 {
435 hr = ParseWxlString(pixn, dwIdx, pWixLoc);
436 ExitOnFailure(hr, "Failed to parse localization string.");
437
438 ++dwIdx;
439 ReleaseNullObject(pixn);
440 }
441
442 hr = S_OK;
443 ExitOnFailure(hr, "Failed to enumerate all localization strings.");
444 }
445
446LExit:
447 if (FAILED(hr) && pWixLoc->rgLocStrings)
448 {
449 for (DWORD idx = 0; idx < pWixLoc->cLocStrings; ++idx)
450 {
451 ReleaseStr(pWixLoc->rgLocStrings[idx].wzId);
452 ReleaseStr(pWixLoc->rgLocStrings[idx].wzText);
453 }
454
455 ReleaseMem(pWixLoc->rgLocStrings);
456 }
457
458 ReleaseObject(pixn);
459 ReleaseObject(pixnl);
460
461 return hr;
462}
463
464static HRESULT ParseWxlControls(
465 __in IXMLDOMElement* pElement,
466 __in WIX_LOCALIZATION* pWixLoc
467 )
468{
469 HRESULT hr = S_OK;
470 IXMLDOMNode* pixn = NULL;
471 IXMLDOMNodeList* pixnl = NULL;
472 DWORD dwIdx = 0;
473
474 hr = XmlSelectNodes(pElement, L"UI|Control", &pixnl);
475 ExitOnLastError(hr, "Failed to get UI child nodes of Wxl File.");
476
477 hr = pixnl->get_length(reinterpret_cast<long*>(&pWixLoc->cLocControls));
478 ExitOnLastError(hr, "Failed to get number of UI child nodes in Wxl File.");
479
480 if (0 < pWixLoc->cLocControls)
481 {
482 pWixLoc->rgLocControls = static_cast<LOC_CONTROL*>(MemAlloc(sizeof(LOC_CONTROL) * pWixLoc->cLocControls, TRUE));
483 ExitOnNull(pWixLoc->rgLocControls, hr, E_OUTOFMEMORY, "Failed to allocate memory for localized controls.");
484
485 while (S_OK == (hr = XmlNextElement(pixnl, &pixn, NULL)))
486 {
487 hr = ParseWxlControl(pixn, dwIdx, pWixLoc);
488 ExitOnFailure(hr, "Failed to parse localized control.");
489
490 ++dwIdx;
491 ReleaseNullObject(pixn);
492 }
493
494 hr = S_OK;
495 ExitOnFailure(hr, "Failed to enumerate all localized controls.");
496 }
497
498LExit:
499 if (FAILED(hr) && pWixLoc->rgLocControls)
500 {
501 for (DWORD idx = 0; idx < pWixLoc->cLocControls; ++idx)
502 {
503 ReleaseStr(pWixLoc->rgLocControls[idx].wzControl);
504 ReleaseStr(pWixLoc->rgLocControls[idx].wzText);
505 }
506
507 ReleaseMem(pWixLoc->rgLocControls);
508 }
509
510 ReleaseObject(pixn);
511 ReleaseObject(pixnl);
512
513 return hr;
514}
515
516static HRESULT ParseWxlString(
517 __in IXMLDOMNode* pixn,
518 __in DWORD dwIdx,
519 __in WIX_LOCALIZATION* pWixLoc
520 )
521{
522 HRESULT hr = S_OK;
523 LOC_STRING* pLocString = NULL;
524 BSTR bstrText = NULL;
525
526 pLocString = pWixLoc->rgLocStrings + dwIdx;
527
528 // Id
529 hr = XmlGetAttribute(pixn, L"Id", &bstrText);
530 ExitOnFailure(hr, "Failed to get Xml attribute Id in Wxl file.");
531
532 hr = StrAllocFormatted(&pLocString->wzId, L"#(loc.%s)", bstrText);
533 ExitOnFailure(hr, "Failed to duplicate Xml attribute Id in Wxl file.");
534
535 ReleaseNullBSTR(bstrText);
536
537 // Overrideable
538 hr = XmlGetAttribute(pixn, L"Overridable", &bstrText);
539 ExitOnFailure(hr, "Failed to get Xml attribute Overridable.");
540
541 if (S_OK == hr)
542 {
543 pLocString->bOverridable = CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrText, -1, L"yes", -1);
544 }
545
546 ReleaseNullBSTR(bstrText);
547
548 // Text
549 hr = XmlGetText(pixn, &bstrText);
550 ExitOnFailure(hr, "Failed to get Xml text in Wxl file.");
551
552 hr = StrAllocString(&pLocString->wzText, bstrText, 0);
553 ExitOnFailure(hr, "Failed to duplicate Xml text in Wxl file.");
554
555LExit:
556 ReleaseBSTR(bstrText);
557
558 return hr;
559}
560
561static HRESULT ParseWxlControl(
562 __in IXMLDOMNode* pixn,
563 __in DWORD dwIdx,
564 __in WIX_LOCALIZATION* pWixLoc
565 )
566{
567 HRESULT hr = S_OK;
568 LOC_CONTROL* pLocControl = NULL;
569 BSTR bstrText = NULL;
570
571 pLocControl = pWixLoc->rgLocControls + dwIdx;
572
573 // Id
574 hr = XmlGetAttribute(pixn, L"Control", &bstrText);
575 ExitOnFailure(hr, "Failed to get Xml attribute Control in Wxl file.");
576
577 hr = StrAllocString(&pLocControl->wzControl, bstrText, 0);
578 ExitOnFailure(hr, "Failed to duplicate Xml attribute Control in Wxl file.");
579
580 ReleaseNullBSTR(bstrText);
581
582 // X
583 pLocControl->nX = LOC_CONTROL_NOT_SET;
584 hr = XmlGetAttributeNumber(pixn, L"X", reinterpret_cast<DWORD*>(&pLocControl->nX));
585 ExitOnFailure(hr, "Failed to get control X attribute.");
586
587 // Y
588 pLocControl->nY = LOC_CONTROL_NOT_SET;
589 hr = XmlGetAttributeNumber(pixn, L"Y", reinterpret_cast<DWORD*>(&pLocControl->nY));
590 ExitOnFailure(hr, "Failed to get control Y attribute.");
591
592 // Width
593 pLocControl->nWidth = LOC_CONTROL_NOT_SET;
594 hr = XmlGetAttributeNumber(pixn, L"Width", reinterpret_cast<DWORD*>(&pLocControl->nWidth));
595 ExitOnFailure(hr, "Failed to get control width attribute.");
596
597 // Height
598 pLocControl->nHeight = LOC_CONTROL_NOT_SET;
599 hr = XmlGetAttributeNumber(pixn, L"Height", reinterpret_cast<DWORD*>(&pLocControl->nHeight));
600 ExitOnFailure(hr, "Failed to get control height attribute.");
601
602 // Text
603 hr = XmlGetText(pixn, &bstrText);
604 ExitOnFailure(hr, "Failed to get control text in Wxl file.");
605
606 hr = StrAllocString(&pLocControl->wzText, bstrText, 0);
607 ExitOnFailure(hr, "Failed to duplicate control text in Wxl file.");
608
609LExit:
610 ReleaseBSTR(bstrText);
611
612 return hr;
613}