diff options
Diffstat (limited to 'src/libs/dutil/WixToolset.DUtil/dutil.cpp')
-rw-r--r-- | src/libs/dutil/WixToolset.DUtil/dutil.cpp | 524 |
1 files changed, 524 insertions, 0 deletions
diff --git a/src/libs/dutil/WixToolset.DUtil/dutil.cpp b/src/libs/dutil/WixToolset.DUtil/dutil.cpp new file mode 100644 index 00000000..56b85207 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/dutil.cpp | |||
@@ -0,0 +1,524 @@ | |||
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 | |||
6 | // Exit macros | ||
7 | #define DExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__) | ||
8 | #define DExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__) | ||
9 | #define DExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__) | ||
10 | #define DExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__) | ||
11 | #define DExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__) | ||
12 | #define DExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__) | ||
13 | #define DExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DUTIL, p, x, e, s, __VA_ARGS__) | ||
14 | #define DExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DUTIL, p, x, s, __VA_ARGS__) | ||
15 | #define DExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DUTIL, p, x, e, s, __VA_ARGS__) | ||
16 | #define DExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DUTIL, p, x, s, __VA_ARGS__) | ||
17 | #define DExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DUTIL, e, x, s, __VA_ARGS__) | ||
18 | #define DExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DUTIL, g, x, s, __VA_ARGS__) | ||
19 | |||
20 | // No need for OACR to warn us about using non-unicode APIs in this file. | ||
21 | #pragma prefast(disable:25068) | ||
22 | |||
23 | // Asserts & Tracing | ||
24 | |||
25 | const int DUTIL_STRING_BUFFER = 1024; | ||
26 | static HMODULE Dutil_hAssertModule = NULL; | ||
27 | static DUTIL_ASSERTDISPLAYFUNCTION Dutil_pfnDisplayAssert = NULL; | ||
28 | static BOOL Dutil_fNoAsserts = FALSE; | ||
29 | static REPORT_LEVEL Dutil_rlCurrentTrace = REPORT_STANDARD; | ||
30 | static BOOL Dutil_fTraceFilenames = FALSE; | ||
31 | static DUTIL_CALLBACK_TRACEERROR vpfnTraceErrorCallback = NULL; | ||
32 | |||
33 | |||
34 | DAPI_(HRESULT) DutilInitialize( | ||
35 | __in_opt DUTIL_CALLBACK_TRACEERROR pfnTraceErrorCallback | ||
36 | ) | ||
37 | { | ||
38 | HRESULT hr = S_OK; | ||
39 | |||
40 | vpfnTraceErrorCallback = pfnTraceErrorCallback; | ||
41 | |||
42 | return hr; | ||
43 | } | ||
44 | |||
45 | |||
46 | DAPI_(void) DutilUninitialize() | ||
47 | { | ||
48 | vpfnTraceErrorCallback = NULL; | ||
49 | } | ||
50 | |||
51 | /******************************************************************* | ||
52 | Dutil_SetAssertModule | ||
53 | |||
54 | *******************************************************************/ | ||
55 | extern "C" void DAPI Dutil_SetAssertModule( | ||
56 | __in HMODULE hAssertModule | ||
57 | ) | ||
58 | { | ||
59 | Dutil_hAssertModule = hAssertModule; | ||
60 | } | ||
61 | |||
62 | |||
63 | /******************************************************************* | ||
64 | Dutil_SetAssertDisplayFunction | ||
65 | |||
66 | *******************************************************************/ | ||
67 | extern "C" void DAPI Dutil_SetAssertDisplayFunction( | ||
68 | __in DUTIL_ASSERTDISPLAYFUNCTION pfn | ||
69 | ) | ||
70 | { | ||
71 | Dutil_pfnDisplayAssert = pfn; | ||
72 | } | ||
73 | |||
74 | |||
75 | /******************************************************************* | ||
76 | Dutil_AssertMsg | ||
77 | |||
78 | *******************************************************************/ | ||
79 | extern "C" void DAPI Dutil_AssertMsg( | ||
80 | __in_z LPCSTR szMessage | ||
81 | ) | ||
82 | { | ||
83 | static BOOL fInAssert = FALSE; // TODO: make this thread safe (this is a cheap hack to prevent re-entrant Asserts) | ||
84 | |||
85 | HRESULT hr = S_OK; | ||
86 | DWORD er = ERROR_SUCCESS; | ||
87 | |||
88 | int id = IDRETRY; | ||
89 | HKEY hkDebug = NULL; | ||
90 | HANDLE hAssertFile = INVALID_HANDLE_VALUE; | ||
91 | char szPath[MAX_PATH] = { }; | ||
92 | DWORD cch = 0; | ||
93 | |||
94 | if (fInAssert) | ||
95 | { | ||
96 | return; | ||
97 | } | ||
98 | fInAssert = TRUE; | ||
99 | |||
100 | char szMsg[DUTIL_STRING_BUFFER]; | ||
101 | hr = ::StringCchCopyA(szMsg, countof(szMsg), szMessage); | ||
102 | DExitOnFailure(hr, "failed to copy message while building assert message"); | ||
103 | |||
104 | if (Dutil_pfnDisplayAssert) | ||
105 | { | ||
106 | // call custom function to display the assert string | ||
107 | if (!Dutil_pfnDisplayAssert(szMsg)) | ||
108 | { | ||
109 | ExitFunction(); | ||
110 | } | ||
111 | } | ||
112 | else | ||
113 | { | ||
114 | OutputDebugStringA(szMsg); | ||
115 | } | ||
116 | |||
117 | if (!Dutil_fNoAsserts) | ||
118 | { | ||
119 | er = ::RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Delivery\\Debug", 0, KEY_QUERY_VALUE, &hkDebug); | ||
120 | if (ERROR_SUCCESS == er) | ||
121 | { | ||
122 | cch = countof(szPath); | ||
123 | er = ::RegQueryValueExA(hkDebug, "DeliveryAssertsLog", NULL, NULL, reinterpret_cast<BYTE*>(szPath), &cch); | ||
124 | szPath[countof(szPath) - 1] = '\0'; // ensure string is null terminated since registry won't guarantee that. | ||
125 | if (ERROR_SUCCESS == er) | ||
126 | { | ||
127 | hAssertFile = ::CreateFileA(szPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); | ||
128 | if (INVALID_HANDLE_VALUE != hAssertFile) | ||
129 | { | ||
130 | if (INVALID_SET_FILE_POINTER != ::SetFilePointer(hAssertFile, 0, 0, FILE_END)) | ||
131 | { | ||
132 | if (SUCCEEDED(::StringCchCatA(szMsg, countof(szMsg), "\r\n"))) | ||
133 | { | ||
134 | ::WriteFile(hAssertFile, szMsg, lstrlenA(szMsg), &cch, NULL); | ||
135 | } | ||
136 | } | ||
137 | } | ||
138 | } | ||
139 | } | ||
140 | |||
141 | // if anything went wrong while fooling around with the registry, just show the usual assert dialog box | ||
142 | if (ERROR_SUCCESS != er) | ||
143 | { | ||
144 | hr = ::StringCchCatA(szMsg, countof(szMsg), "\nAbort=Debug, Retry=Skip, Ignore=Skip all"); | ||
145 | DExitOnFailure(hr, "failed to concat string while building assert message"); | ||
146 | |||
147 | id = ::MessageBoxA(0, szMsg, "Debug Assert Message", | ||
148 | MB_SERVICE_NOTIFICATION | MB_TOPMOST | | ||
149 | MB_DEFBUTTON2 | MB_ABORTRETRYIGNORE); | ||
150 | } | ||
151 | } | ||
152 | |||
153 | if (id == IDABORT) | ||
154 | { | ||
155 | if (Dutil_hAssertModule) | ||
156 | { | ||
157 | ::GetModuleFileNameA(Dutil_hAssertModule, szPath, countof(szPath)); | ||
158 | |||
159 | hr = ::StringCchPrintfA(szMsg, countof(szMsg), "Module is running from: %s\nIf you are not using pdb-stamping, place your PDB near the module and attach to process id: %d (0x%x)", szPath, ::GetCurrentProcessId(), ::GetCurrentProcessId()); | ||
160 | if (SUCCEEDED(hr)) | ||
161 | { | ||
162 | ::MessageBoxA(0, szMsg, "Debug Assert Message", MB_SERVICE_NOTIFICATION | MB_TOPMOST | MB_OK); | ||
163 | } | ||
164 | } | ||
165 | |||
166 | ::DebugBreak(); | ||
167 | } | ||
168 | else if (id == IDIGNORE) | ||
169 | { | ||
170 | Dutil_fNoAsserts = TRUE; | ||
171 | } | ||
172 | |||
173 | LExit: | ||
174 | ReleaseFileHandle(hAssertFile); | ||
175 | ReleaseRegKey(hkDebug); | ||
176 | fInAssert = FALSE; | ||
177 | } | ||
178 | |||
179 | |||
180 | /******************************************************************* | ||
181 | Dutil_Assert | ||
182 | |||
183 | *******************************************************************/ | ||
184 | extern "C" void DAPI Dutil_Assert( | ||
185 | __in_z LPCSTR szFile, | ||
186 | __in int iLine | ||
187 | ) | ||
188 | { | ||
189 | HRESULT hr = S_OK; | ||
190 | char szMessage[DUTIL_STRING_BUFFER] = { }; | ||
191 | hr = ::StringCchPrintfA(szMessage, countof(szMessage), "Assertion failed in %s, %i", szFile, iLine); | ||
192 | if (SUCCEEDED(hr)) | ||
193 | { | ||
194 | Dutil_AssertMsg(szMessage); | ||
195 | } | ||
196 | else | ||
197 | { | ||
198 | Dutil_AssertMsg("Assert failed to build string"); | ||
199 | } | ||
200 | } | ||
201 | |||
202 | |||
203 | /******************************************************************* | ||
204 | Dutil_AssertSz | ||
205 | |||
206 | *******************************************************************/ | ||
207 | extern "C" void DAPI Dutil_AssertSz( | ||
208 | __in_z LPCSTR szFile, | ||
209 | __in int iLine, | ||
210 | __in_z __format_string LPCSTR szMsg | ||
211 | ) | ||
212 | { | ||
213 | HRESULT hr = S_OK; | ||
214 | char szMessage[DUTIL_STRING_BUFFER] = { }; | ||
215 | |||
216 | hr = ::StringCchPrintfA(szMessage, countof(szMessage), "Assertion failed in %s, %i\n%s", szFile, iLine, szMsg); | ||
217 | if (SUCCEEDED(hr)) | ||
218 | { | ||
219 | Dutil_AssertMsg(szMessage); | ||
220 | } | ||
221 | else | ||
222 | { | ||
223 | Dutil_AssertMsg("Assert failed to build string"); | ||
224 | } | ||
225 | } | ||
226 | |||
227 | |||
228 | /******************************************************************* | ||
229 | Dutil_TraceSetLevel | ||
230 | |||
231 | *******************************************************************/ | ||
232 | extern "C" void DAPI Dutil_TraceSetLevel( | ||
233 | __in REPORT_LEVEL rl, | ||
234 | __in BOOL fTraceFilenames | ||
235 | ) | ||
236 | { | ||
237 | Dutil_rlCurrentTrace = rl; | ||
238 | Dutil_fTraceFilenames = fTraceFilenames; | ||
239 | } | ||
240 | |||
241 | |||
242 | /******************************************************************* | ||
243 | Dutil_TraceGetLevel | ||
244 | |||
245 | *******************************************************************/ | ||
246 | extern "C" REPORT_LEVEL DAPI Dutil_TraceGetLevel() | ||
247 | { | ||
248 | return Dutil_rlCurrentTrace; | ||
249 | } | ||
250 | |||
251 | |||
252 | /******************************************************************* | ||
253 | Dutil_Trace | ||
254 | |||
255 | *******************************************************************/ | ||
256 | extern "C" void DAPIV Dutil_Trace( | ||
257 | __in_z LPCSTR szFile, | ||
258 | __in int iLine, | ||
259 | __in REPORT_LEVEL rl, | ||
260 | __in_z __format_string LPCSTR szFormat, | ||
261 | ... | ||
262 | ) | ||
263 | { | ||
264 | AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid tracing level"); | ||
265 | |||
266 | HRESULT hr = S_OK; | ||
267 | char szOutput[DUTIL_STRING_BUFFER] = { }; | ||
268 | char szMsg[DUTIL_STRING_BUFFER] = { }; | ||
269 | |||
270 | if (Dutil_rlCurrentTrace < rl) | ||
271 | { | ||
272 | return; | ||
273 | } | ||
274 | |||
275 | va_list args; | ||
276 | va_start(args, szFormat); | ||
277 | hr = ::StringCchVPrintfA(szOutput, countof(szOutput), szFormat, args); | ||
278 | va_end(args); | ||
279 | |||
280 | if (SUCCEEDED(hr)) | ||
281 | { | ||
282 | LPCSTR szPrefix = "Trace/u"; | ||
283 | switch (rl) | ||
284 | { | ||
285 | case REPORT_STANDARD: | ||
286 | szPrefix = "Trace/s"; | ||
287 | break; | ||
288 | case REPORT_VERBOSE: | ||
289 | szPrefix = "Trace/v"; | ||
290 | break; | ||
291 | case REPORT_DEBUG: | ||
292 | szPrefix = "Trace/d"; | ||
293 | break; | ||
294 | } | ||
295 | |||
296 | if (Dutil_fTraceFilenames) | ||
297 | { | ||
298 | hr = ::StringCchPrintfA(szMsg, countof(szMsg), "%s [%s,%d]: %s\r\n", szPrefix, szFile, iLine, szOutput); | ||
299 | } | ||
300 | else | ||
301 | { | ||
302 | hr = ::StringCchPrintfA(szMsg, countof(szMsg), "%s: %s\r\n", szPrefix, szOutput); | ||
303 | } | ||
304 | |||
305 | if (SUCCEEDED(hr)) | ||
306 | { | ||
307 | OutputDebugStringA(szMsg); | ||
308 | } | ||
309 | // else fall through to the case below | ||
310 | } | ||
311 | |||
312 | if (FAILED(hr)) | ||
313 | { | ||
314 | if (Dutil_fTraceFilenames) | ||
315 | { | ||
316 | ::StringCchPrintfA(szMsg, countof(szMsg), "Trace [%s,%d]: message too long, skipping\r\n", szFile, iLine); | ||
317 | } | ||
318 | else | ||
319 | { | ||
320 | ::StringCchPrintfA(szMsg, countof(szMsg), "Trace: message too long, skipping\r\n"); | ||
321 | } | ||
322 | |||
323 | szMsg[countof(szMsg)-1] = '\0'; | ||
324 | OutputDebugStringA(szMsg); | ||
325 | } | ||
326 | } | ||
327 | |||
328 | |||
329 | /******************************************************************* | ||
330 | Dutil_TraceError | ||
331 | |||
332 | *******************************************************************/ | ||
333 | extern "C" void DAPIV Dutil_TraceError( | ||
334 | __in_z LPCSTR szFile, | ||
335 | __in int iLine, | ||
336 | __in REPORT_LEVEL rl, | ||
337 | __in HRESULT hrError, | ||
338 | __in_z __format_string LPCSTR szFormat, | ||
339 | ... | ||
340 | ) | ||
341 | { | ||
342 | HRESULT hr = S_OK; | ||
343 | char szOutput[DUTIL_STRING_BUFFER] = { }; | ||
344 | char szMsg[DUTIL_STRING_BUFFER] = { }; | ||
345 | |||
346 | // if this is NOT an error report and we're not logging at this level, bail | ||
347 | if (REPORT_ERROR != rl && Dutil_rlCurrentTrace < rl) | ||
348 | { | ||
349 | return; | ||
350 | } | ||
351 | |||
352 | va_list args; | ||
353 | va_start(args, szFormat); | ||
354 | hr = ::StringCchVPrintfA(szOutput, countof(szOutput), szFormat, args); | ||
355 | va_end(args); | ||
356 | |||
357 | if (SUCCEEDED(hr)) | ||
358 | { | ||
359 | if (Dutil_fTraceFilenames) | ||
360 | { | ||
361 | if (FAILED(hrError)) | ||
362 | { | ||
363 | hr = ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError 0x%x [%s,%d]: %s\r\n", hrError, szFile, iLine, szOutput); | ||
364 | } | ||
365 | else | ||
366 | { | ||
367 | hr = ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError [%s,%d]: %s\r\n", szFile, iLine, szOutput); | ||
368 | } | ||
369 | } | ||
370 | else | ||
371 | { | ||
372 | if (FAILED(hrError)) | ||
373 | { | ||
374 | hr = ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError 0x%x: %s\r\n", hrError, szOutput); | ||
375 | } | ||
376 | else | ||
377 | { | ||
378 | hr = ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError: %s\r\n", szOutput); | ||
379 | } | ||
380 | } | ||
381 | |||
382 | if (SUCCEEDED(hr)) | ||
383 | { | ||
384 | OutputDebugStringA(szMsg); | ||
385 | } | ||
386 | // else fall through to the failure case below | ||
387 | } | ||
388 | |||
389 | if (FAILED(hr)) | ||
390 | { | ||
391 | if (Dutil_fTraceFilenames) | ||
392 | { | ||
393 | if (FAILED(hrError)) | ||
394 | { | ||
395 | ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError 0x%x [%s,%d]: message too long, skipping\r\n", hrError, szFile, iLine); | ||
396 | } | ||
397 | else | ||
398 | { | ||
399 | ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError [%s,%d]: message too long, skipping\r\n", szFile, iLine); | ||
400 | } | ||
401 | } | ||
402 | else | ||
403 | { | ||
404 | if (FAILED(hrError)) | ||
405 | { | ||
406 | ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError 0x%x: message too long, skipping\r\n", hrError); | ||
407 | } | ||
408 | else | ||
409 | { | ||
410 | ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError: message too long, skipping\r\n"); | ||
411 | } | ||
412 | } | ||
413 | |||
414 | szMsg[countof(szMsg)-1] = '\0'; | ||
415 | OutputDebugStringA(szMsg); | ||
416 | } | ||
417 | } | ||
418 | |||
419 | |||
420 | DAPIV_(void) Dutil_TraceErrorSource( | ||
421 | __in_z LPCSTR szFile, | ||
422 | __in int iLine, | ||
423 | __in REPORT_LEVEL rl, | ||
424 | __in UINT source, | ||
425 | __in HRESULT hr, | ||
426 | __in_z __format_string LPCSTR szFormat, | ||
427 | ... | ||
428 | ) | ||
429 | { | ||
430 | // if this is NOT an error report and we're not logging at this level, bail | ||
431 | if (REPORT_ERROR != rl && Dutil_rlCurrentTrace < rl) | ||
432 | { | ||
433 | return; | ||
434 | } | ||
435 | |||
436 | if (DUTIL_SOURCE_UNKNOWN != source && vpfnTraceErrorCallback) | ||
437 | { | ||
438 | va_list args; | ||
439 | va_start(args, szFormat); | ||
440 | vpfnTraceErrorCallback(szFile, iLine, rl, source, hr, szFormat, args); | ||
441 | va_end(args); | ||
442 | } | ||
443 | } | ||
444 | |||
445 | |||
446 | /******************************************************************* | ||
447 | Dutil_RootFailure | ||
448 | |||
449 | *******************************************************************/ | ||
450 | extern "C" void DAPI Dutil_RootFailure( | ||
451 | __in_z LPCSTR szFile, | ||
452 | __in int iLine, | ||
453 | __in HRESULT hrError | ||
454 | ) | ||
455 | { | ||
456 | #ifndef DEBUG | ||
457 | UNREFERENCED_PARAMETER(szFile); | ||
458 | UNREFERENCED_PARAMETER(iLine); | ||
459 | UNREFERENCED_PARAMETER(hrError); | ||
460 | #endif // DEBUG | ||
461 | |||
462 | TraceError(hrError, "Root failure at %s:%d", szFile, iLine); | ||
463 | } | ||
464 | |||
465 | /******************************************************************* | ||
466 | LoadSystemLibrary - Fully qualifies the path to a module in the | ||
467 | Windows system directory and loads it. | ||
468 | |||
469 | Returns | ||
470 | E_MODNOTFOUND - The module could not be found. | ||
471 | * - Another error occured. | ||
472 | ********************************************************************/ | ||
473 | extern "C" HRESULT DAPI LoadSystemLibrary( | ||
474 | __in_z LPCWSTR wzModuleName, | ||
475 | __out HMODULE *phModule | ||
476 | ) | ||
477 | { | ||
478 | HRESULT hr = LoadSystemLibraryWithPath(wzModuleName, phModule, NULL); | ||
479 | return hr; | ||
480 | } | ||
481 | |||
482 | /******************************************************************* | ||
483 | LoadSystemLibraryWithPath - Fully qualifies the path to a module in | ||
484 | the Windows system directory and loads it | ||
485 | and returns the path | ||
486 | |||
487 | Returns | ||
488 | E_MODNOTFOUND - The module could not be found. | ||
489 | * - Another error occured. | ||
490 | ********************************************************************/ | ||
491 | extern "C" HRESULT DAPI LoadSystemLibraryWithPath( | ||
492 | __in_z LPCWSTR wzModuleName, | ||
493 | __out HMODULE *phModule, | ||
494 | __deref_out_z_opt LPWSTR* psczPath | ||
495 | ) | ||
496 | { | ||
497 | HRESULT hr = S_OK; | ||
498 | DWORD cch = 0; | ||
499 | WCHAR wzPath[MAX_PATH] = { }; | ||
500 | |||
501 | cch = ::GetSystemDirectoryW(wzPath, MAX_PATH); | ||
502 | DExitOnNullWithLastError(cch, hr, "Failed to get the Windows system directory."); | ||
503 | |||
504 | if (L'\\' != wzPath[cch - 1]) | ||
505 | { | ||
506 | hr = ::StringCchCatNW(wzPath, MAX_PATH, L"\\", 1); | ||
507 | DExitOnRootFailure(hr, "Failed to terminate the string with a backslash."); | ||
508 | } | ||
509 | |||
510 | hr = ::StringCchCatW(wzPath, MAX_PATH, wzModuleName); | ||
511 | DExitOnRootFailure(hr, "Failed to create the fully-qualified path to %ls.", wzModuleName); | ||
512 | |||
513 | *phModule = ::LoadLibraryW(wzPath); | ||
514 | DExitOnNullWithLastError(*phModule, hr, "Failed to load the library %ls.", wzModuleName); | ||
515 | |||
516 | if (psczPath) | ||
517 | { | ||
518 | hr = StrAllocString(psczPath, wzPath, MAX_PATH); | ||
519 | DExitOnFailure(hr, "Failed to copy the path to library."); | ||
520 | } | ||
521 | |||
522 | LExit: | ||
523 | return hr; | ||
524 | } | ||