1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4//*****************************************************************************
5// MSCoree.cpp
6//*****************************************************************************
7#include "stdafx.h" // Standard header.
8
9#include <utilcode.h> // Utility helpers.
10#include <posterror.h> // Error handlers
11#define INIT_GUIDS
12#include <corpriv.h>
13#include <winwrap.h>
14#include <mscoree.h>
15#include "shimload.h"
16#include "metadataexports.h"
17#include "ex.h"
18
19#include "product_version.h"
20
21#ifdef FEATURE_COMINTEROP
22#include "ComCallUnmarshal.h"
23#endif // FEATURE_COMINTEROP
24
25#include "clrprivhosting.h"
26
27#ifdef FEATURE_PROFAPI_ATTACH_DETACH
28#include "../../vm/profattach.h"
29#endif // FEATURE_PROFAPI_ATTACH_DETACH
30
31#include <dbgenginemetrics.h>
32
33// Locals.
34BOOL STDMETHODCALLTYPE EEDllMain( // TRUE on success, FALSE on error.
35 HINSTANCE hInst, // Instance handle of the loaded module.
36 DWORD dwReason, // Reason for loading.
37 LPVOID lpReserved); // Unused.
38
39// Globals.
40HINSTANCE g_hThisInst; // This library.
41
42#ifndef CROSSGEN_COMPILE
43//*****************************************************************************
44// Handle lifetime of loaded library.
45//*****************************************************************************
46
47#include <shlwapi.h>
48
49#include <process.h> // for __security_init_cookie()
50
51extern "C" IExecutionEngine* IEE();
52
53#ifdef NO_CRT_INIT
54#define _CRT_INIT(hInstance, dwReason, lpReserved) (TRUE)
55#else
56extern "C" BOOL WINAPI _CRT_INIT(HANDLE hInstance, DWORD dwReason, LPVOID lpReserved);
57#endif
58
59extern "C" BOOL WINAPI DllMain(HANDLE hInstance, DWORD dwReason, LPVOID lpReserved);
60
61// For the CoreClr, this is the real DLL entrypoint. We make ourselves the first entrypoint as
62// we need to capture coreclr's hInstance before the C runtime initializes. This function
63// will capture hInstance, let the C runtime initialize and then invoke the "classic"
64// DllMain that initializes everything else.
65extern "C" BOOL WINAPI CoreDllMain(HANDLE hInstance, DWORD dwReason, LPVOID lpReserved)
66{
67 STATIC_CONTRACT_NOTHROW;
68
69 BOOL result;
70 switch (dwReason)
71 {
72 case DLL_PROCESS_ATTACH:
73#ifndef FEATURE_PAL
74 // Make sure the /GS security cookie is initialized before we call anything else.
75 // BinScope detects the call to __security_init_cookie in its "Has Non-GS-friendly
76 // Initialization" check and makes it pass.
77 __security_init_cookie();
78#endif // FEATURE_PAL
79
80 // It's critical that we invoke InitUtilCode() before the CRT initializes.
81 // We have a lot of global ctors that will break if we let the CRT initialize without
82 // this step having been done.
83
84 CoreClrCallbacks cccallbacks;
85 cccallbacks.m_hmodCoreCLR = (HINSTANCE)hInstance;
86 cccallbacks.m_pfnIEE = IEE;
87 cccallbacks.m_pfnGetCORSystemDirectory = GetCORSystemDirectoryInternaL;
88 cccallbacks.m_pfnGetCLRFunction = GetCLRFunction;
89 InitUtilcode(cccallbacks);
90
91 if (!(result = _CRT_INIT(hInstance, dwReason, lpReserved)))
92 {
93 // CRT_INIT may fail to initialize the CRT heap. Make sure we don't continue
94 // down a path that would trigger an AV and tear down the host process
95 break;
96 }
97 result = DllMain(hInstance, dwReason, lpReserved);
98 break;
99
100 case DLL_THREAD_ATTACH:
101 _CRT_INIT(hInstance, dwReason, lpReserved);
102 result = DllMain(hInstance, dwReason, lpReserved);
103 break;
104
105 case DLL_PROCESS_DETACH: // intentional fallthru
106 case DLL_THREAD_DETACH:
107 result = DllMain(hInstance, dwReason, lpReserved);
108 _CRT_INIT(hInstance, dwReason, lpReserved);
109 break;
110
111 default:
112 result = FALSE; // it'd be an OS bug if we got here - not much we can do.
113 break;
114 }
115 return result;
116}
117
118extern "C"
119BOOL WINAPI DllMain(HANDLE hInstance, DWORD dwReason, LPVOID lpReserved)
120{
121 STATIC_CONTRACT_NOTHROW;
122
123 switch (dwReason)
124 {
125 case DLL_PROCESS_ATTACH:
126 {
127 // Save the module handle.
128 g_hThisInst = (HINSTANCE)hInstance;
129
130 // Prevent buffer-overruns
131 // If buffer is overrun, it is possible the saved callback has been trashed.
132 // The callback is unsafe.
133 //SetBufferOverrunHandler();
134 if (!EEDllMain((HINSTANCE)hInstance, dwReason, lpReserved))
135 {
136 return FALSE;
137 }
138 }
139 break;
140
141 case DLL_PROCESS_DETACH:
142 {
143 EEDllMain((HINSTANCE)hInstance, dwReason, lpReserved);
144 }
145 break;
146
147 case DLL_THREAD_DETACH:
148 {
149 EEDllMain((HINSTANCE)hInstance, dwReason, lpReserved);
150 }
151 break;
152 }
153
154 return TRUE;
155}
156
157#ifdef FEATURE_COMINTEROP
158// ---------------------------------------------------------------------------
159// %%Function: DllCanUnloadNowInternal
160//
161// Returns:
162// S_FALSE - Indicating that COR, once loaded, may not be
163// unloaded.
164// ---------------------------------------------------------------------------
165STDAPI DllCanUnloadNowInternal(void)
166{
167 STATIC_CONTRACT_NOTHROW;
168 STATIC_CONTRACT_ENTRY_POINT;
169
170 //we should never unload unless the process is dying
171 return S_FALSE;
172} // DllCanUnloadNowInternal
173
174// ---------------------------------------------------------------------------
175// %%Function: DllRegisterServerInternal
176//
177// Description:
178// Registers
179// ---------------------------------------------------------------------------
180STDAPI DllRegisterServerInternal(HINSTANCE hMod, LPCWSTR version)
181{
182
183 CONTRACTL{
184 NOTHROW;
185 GC_NOTRIGGER;
186 ENTRY_POINT;
187 PRECONDITION(CheckPointer(version));
188 } CONTRACTL_END;
189
190 return S_OK;
191} // DllRegisterServerInternal
192
193// ---------------------------------------------------------------------------
194// %%Function: DllUnregisterServerInternal
195// ---------------------------------------------------------------------------
196STDAPI DllUnregisterServerInternal(void)
197{
198
199 CONTRACTL
200 {
201 GC_NOTRIGGER;
202 NOTHROW;
203 ENTRY_POINT;
204 }
205 CONTRACTL_END;
206
207 return S_OK;
208
209} // DllUnregisterServerInternal
210#endif // FEATURE_COMINTEROP
211
212#endif // CROSSGEN_COMPILE
213
214HINSTANCE GetModuleInst()
215{
216 LIMITED_METHOD_CONTRACT;
217 return (g_hThisInst);
218}
219
220// ---------------------------------------------------------------------------
221// %%Function: MetaDataGetDispenser
222// This function gets the Dispenser interface given the CLSID and REFIID.
223// ---------------------------------------------------------------------------
224STDAPI MetaDataGetDispenser( // Return HRESULT
225 REFCLSID rclsid, // The class to desired.
226 REFIID riid, // Interface wanted on class factory.
227 LPVOID FAR *ppv) // Return interface pointer here.
228{
229
230 CONTRACTL {
231 NOTHROW;
232 GC_NOTRIGGER;
233 ENTRY_POINT;
234 PRECONDITION(CheckPointer(ppv));
235 } CONTRACTL_END;
236
237 NonVMComHolder<IClassFactory> pcf(NULL);
238 HRESULT hr;
239 BEGIN_ENTRYPOINT_NOTHROW;
240
241 IfFailGo(MetaDataDllGetClassObject(rclsid, IID_IClassFactory, (void **) &pcf));
242 hr = pcf->CreateInstance(NULL, riid, ppv);
243
244ErrExit:
245 END_ENTRYPOINT_NOTHROW;
246
247 return (hr);
248}
249
250// ---------------------------------------------------------------------------
251// %%Function: GetMetaDataInternalInterface
252// This function gets the IMDInternalImport given the metadata on memory.
253// ---------------------------------------------------------------------------
254STDAPI GetMetaDataInternalInterface(
255 LPVOID pData, // [IN] in memory metadata section
256 ULONG cbData, // [IN] size of the metadata section
257 DWORD flags, // [IN] MDInternal_OpenForRead or MDInternal_OpenForENC
258 REFIID riid, // [IN] desired interface
259 void **ppv) // [OUT] returned interface
260{
261 CONTRACTL{
262 NOTHROW;
263 GC_NOTRIGGER;
264 ENTRY_POINT;
265 PRECONDITION(CheckPointer(pData));
266 PRECONDITION(CheckPointer(ppv));
267 } CONTRACTL_END;
268
269 HRESULT hr = S_OK;
270 BEGIN_ENTRYPOINT_NOTHROW;
271
272 hr = GetMDInternalInterface(pData, cbData, flags, riid, ppv);
273
274 END_ENTRYPOINT_NOTHROW;
275 return hr;
276}
277
278// ---------------------------------------------------------------------------
279// %%Function: GetMetaDataInternalInterfaceFromPublic
280// This function gets the internal scopeless interface given the public
281// scopeless interface.
282// ---------------------------------------------------------------------------
283STDAPI GetMetaDataInternalInterfaceFromPublic(
284 IUnknown *pv, // [IN] Given interface.
285 REFIID riid, // [IN] desired interface
286 void **ppv) // [OUT] returned interface
287{
288 CONTRACTL{
289 NOTHROW;
290 GC_NOTRIGGER;
291 ENTRY_POINT;
292 PRECONDITION(CheckPointer(pv));
293 PRECONDITION(CheckPointer(ppv));
294 } CONTRACTL_END;
295
296 HRESULT hr = S_OK;
297 BEGIN_ENTRYPOINT_NOTHROW;
298
299 hr = GetMDInternalInterfaceFromPublic(pv, riid, ppv);
300
301 END_ENTRYPOINT_NOTHROW;
302 return hr;
303}
304
305// ---------------------------------------------------------------------------
306// %%Function: GetMetaDataPublicInterfaceFromInternal
307// This function gets the public scopeless interface given the internal
308// scopeless interface.
309// ---------------------------------------------------------------------------
310STDAPI GetMetaDataPublicInterfaceFromInternal(
311 void *pv, // [IN] Given interface.
312 REFIID riid, // [IN] desired interface.
313 void **ppv) // [OUT] returned interface
314{
315 CONTRACTL{
316 NOTHROW;
317 GC_NOTRIGGER;
318 PRECONDITION(CheckPointer(pv));
319 PRECONDITION(CheckPointer(ppv));
320 ENTRY_POINT;
321 } CONTRACTL_END;
322
323 HRESULT hr = S_OK;
324 BEGIN_ENTRYPOINT_NOTHROW;
325
326 hr = GetMDPublicInterfaceFromInternal(pv, riid, ppv);
327
328 END_ENTRYPOINT_NOTHROW;
329 return hr;
330}
331
332
333// ---------------------------------------------------------------------------
334// %%Function: ReopenMetaDataWithMemory
335// This function gets the public scopeless interface given the internal
336// scopeless interface.
337// ---------------------------------------------------------------------------
338STDAPI ReOpenMetaDataWithMemory(
339 void *pUnk, // [IN] Given scope. public interfaces
340 LPCVOID pData, // [in] Location of scope data.
341 ULONG cbData) // [in] Size of the data pointed to by pData.
342{
343 CONTRACTL{
344 NOTHROW;
345 GC_NOTRIGGER;
346 ENTRY_POINT;
347 PRECONDITION(CheckPointer(pUnk));
348 PRECONDITION(CheckPointer(pData));
349 } CONTRACTL_END;
350
351 HRESULT hr = S_OK;
352
353 BEGIN_ENTRYPOINT_NOTHROW;
354 hr = MDReOpenMetaDataWithMemory(pUnk, pData, cbData);
355 END_ENTRYPOINT_NOTHROW;
356 return hr;
357}
358
359// ---------------------------------------------------------------------------
360// %%Function: ReopenMetaDataWithMemoryEx
361// This function gets the public scopeless interface given the internal
362// scopeless interface.
363// ---------------------------------------------------------------------------
364STDAPI ReOpenMetaDataWithMemoryEx(
365 void *pUnk, // [IN] Given scope. public interfaces
366 LPCVOID pData, // [in] Location of scope data.
367 ULONG cbData, // [in] Size of the data pointed to by pData.
368 DWORD dwReOpenFlags) // [in] ReOpen flags
369{
370 CONTRACTL{
371 NOTHROW;
372 GC_NOTRIGGER;
373 ENTRY_POINT;
374 PRECONDITION(CheckPointer(pUnk));
375 PRECONDITION(CheckPointer(pData));
376 } CONTRACTL_END;
377
378 HRESULT hr = S_OK;
379
380 BEGIN_ENTRYPOINT_NOTHROW;
381 hr = MDReOpenMetaDataWithMemoryEx(pUnk, pData, cbData, dwReOpenFlags);
382 END_ENTRYPOINT_NOTHROW;
383 return hr;
384}
385
386
387#ifndef CROSSGEN_COMPILE
388// ---------------------------------------------------------------------------
389// %%Function: CoInitializeCor
390//
391// Parameters:
392// fFlags - Initialization flags for the engine. See the
393// COINITICOR enumerator for valid values.
394//
395// Returns:
396// S_OK - On success
397//
398// Description:
399// Reserved to initialize the Cor runtime engine explicitly. This currently
400// does nothing.
401// ---------------------------------------------------------------------------
402STDAPI CoInitializeCor(DWORD fFlags)
403{
404 WRAPPER_NO_CONTRACT;
405
406 BEGIN_ENTRYPOINT_NOTHROW;
407
408 // Since the CLR doesn't currently support being unloaded, we don't hold a ref
409 // count and don't even pretend to try to unload.
410 END_ENTRYPOINT_NOTHROW;
411
412 return (S_OK);
413}
414
415// ---------------------------------------------------------------------------
416// %%Function: CoUninitializeCor
417//
418// Parameters:
419// none
420//
421// Returns:
422// Nothing
423//
424// Description:
425// Function to indicate the client is done with the CLR. This currently does
426// nothing.
427// ---------------------------------------------------------------------------
428STDAPI_(void) CoUninitializeCor(void)
429{
430 WRAPPER_NO_CONTRACT;
431
432 BEGIN_ENTRYPOINT_VOIDRET;
433
434 // Since the CLR doesn't currently support being unloaded, we don't hold a ref
435 // count and don't even pretend to try to unload.
436 END_ENTRYPOINT_VOIDRET;
437
438}
439
440// Undef LoadStringRC & LoadStringRCEx so we can export these functions.
441#undef LoadStringRC
442#undef LoadStringRCEx
443
444// ---------------------------------------------------------------------------
445// %%Function: LoadStringRC
446//
447// Parameters:
448// none
449//
450// Returns:
451// Nothing
452//
453// Description:
454// Function to load a resource based on it's ID.
455// ---------------------------------------------------------------------------
456STDAPI LoadStringRC(
457 UINT iResourceID,
458 __out_ecount(iMax) __out_z LPWSTR szBuffer,
459 int iMax,
460 int bQuiet
461)
462{
463 WRAPPER_NO_CONTRACT;
464
465 HRESULT hr = S_OK;
466
467 if (NULL == szBuffer)
468 return E_INVALIDARG;
469 if (0 == iMax)
470 return E_INVALIDARG;
471
472 BEGIN_ENTRYPOINT_NOTHROW;
473 hr = UtilLoadStringRC(iResourceID, szBuffer, iMax, bQuiet);
474 END_ENTRYPOINT_NOTHROW;
475 return hr;
476}
477
478// ---------------------------------------------------------------------------
479// %%Function: LoadStringRCEx
480//
481// Parameters:
482// none
483//
484// Returns:
485// Nothing
486//
487// Description:
488// Ex version of the function to load a resource based on it's ID.
489// ---------------------------------------------------------------------------
490#ifdef FEATURE_USE_LCID
491STDAPI LoadStringRCEx(
492 LCID lcid,
493 UINT iResourceID,
494 __out_ecount(iMax) __out_z LPWSTR szBuffer,
495 int iMax,
496 int bQuiet,
497 int *pcwchUsed
498)
499{
500 WRAPPER_NO_CONTRACT;
501 HRESULT hr = S_OK;
502
503 if (NULL == szBuffer)
504 return E_INVALIDARG;
505 if (0 == iMax)
506 return E_INVALIDARG;
507
508 BEGIN_ENTRYPOINT_NOTHROW;
509 hr = UtilLoadStringRCEx(lcid, iResourceID, szBuffer, iMax, bQuiet, pcwchUsed);
510 END_ENTRYPOINT_NOTHROW;
511 return hr;
512}
513#endif
514// Redefine them as errors to prevent people from using these from inside the rest of the compilation unit.
515#define LoadStringRC __error("From inside the CLR, use UtilLoadStringRC; LoadStringRC is only meant to be exported.")
516#define LoadStringRCEx __error("From inside the CLR, use UtilLoadStringRCEx; LoadStringRC is only meant to be exported.")
517
518#endif // CROSSGEN_COMPILE
519
520
521// Replacement for legacy shim API GetCORRequiredVersion(...) used in linked libraries.
522// Used in code:TiggerStorage::GetDefaultVersion#CallTo_CLRRuntimeHostInternal_GetImageVersionString.
523HRESULT
524CLRRuntimeHostInternal_GetImageVersionString(
525 __out_ecount_opt(*pcchBuffer) LPWSTR wszBuffer,
526 __inout DWORD *pcchBuffer)
527{
528 // Simply forward the call to the ICLRRuntimeHostInternal implementation.
529 STATIC_CONTRACT_WRAPPER;
530
531 HRESULT hr = GetCORVersionInternal(wszBuffer, *pcchBuffer, pcchBuffer);
532
533 return hr;
534} // CLRRuntimeHostInternal_GetImageVersionString
535
536STDAPI GetCORSystemDirectoryInternaL(SString& pBuffer)
537{
538 CONTRACTL {
539 NOTHROW;
540 GC_NOTRIGGER;
541 ENTRY_POINT;
542 } CONTRACTL_END;
543
544 HRESULT hr = S_OK;
545 BEGIN_ENTRYPOINT_NOTHROW;
546
547
548#ifdef CROSSGEN_COMPILE
549
550 if (WszGetModuleFileName(NULL, pBuffer) > 0)
551 {
552 hr = CopySystemDirectory(pBuffer, pBuffer);
553 }
554 else {
555 hr = HRESULT_FROM_GetLastError();
556 }
557
558#else
559
560 if (!PAL_GetPALDirectoryWrapper(pBuffer)) {
561 hr = HRESULT_FROM_GetLastError();
562 }
563#endif
564
565 END_ENTRYPOINT_NOTHROW;
566 return hr;
567}
568
569//
570// Returns version of the runtime (null-terminated).
571//
572// Arguments:
573// pBuffer - [out] Output buffer allocated by caller of size cchBuffer.
574// cchBuffer - Size of pBuffer in characters.
575// pdwLength - [out] Size of the version string in characters (incl. null-terminator). Will be filled
576// even if ERROR_INSUFFICIENT_BUFFER is returned.
577//
578// Return Value:
579// S_OK - Output buffer contains the version string.
580// HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) - *pdwLength contains required size of the buffer in
581// characters.
582
583STDAPI GetCORVersionInternal(
584__out_ecount_z_opt(cchBuffer) LPWSTR pBuffer,
585 DWORD cchBuffer,
586 __out DWORD *pdwLength)
587{
588 CONTRACTL {
589 NOTHROW;
590 GC_NOTRIGGER;
591 ENTRY_POINT;
592 PRECONDITION(CheckPointer(pBuffer, NULL_OK));
593 PRECONDITION(CheckPointer(pdwLength));
594 } CONTRACTL_END;
595
596 HRESULT hr;
597 BEGIN_ENTRYPOINT_NOTHROW;
598
599 if ((pBuffer != NULL) && (cchBuffer > 0))
600 { // Initialize the output for case the function fails
601 *pBuffer = W('\0');
602 }
603
604#define VERSION_NUMBER_NOSHIM W("v") QUOTE_MACRO_L(CLR_MAJOR_VERSION.CLR_MINOR_VERSION.CLR_BUILD_VERSION)
605
606 DWORD length = (DWORD)(wcslen(VERSION_NUMBER_NOSHIM) + 1);
607 if (length > cchBuffer)
608 {
609 hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
610 }
611 else
612 {
613 if (pBuffer == NULL)
614 {
615 hr = E_POINTER;
616 }
617 else
618 {
619 CopyMemory(pBuffer, VERSION_NUMBER_NOSHIM, length * sizeof(WCHAR));
620 hr = S_OK;
621 }
622 }
623 *pdwLength = length;
624
625 END_ENTRYPOINT_NOTHROW;
626 return hr;
627
628}
629
630static DWORD g_dwSystemDirectory = 0;
631static WCHAR * g_pSystemDirectory = NULL;
632
633HRESULT GetInternalSystemDirectory(__out_ecount_part_opt(*pdwLength,*pdwLength) LPWSTR buffer, __inout DWORD* pdwLength)
634{
635 CONTRACTL {
636 NOTHROW;
637 GC_NOTRIGGER;
638 PRECONDITION(CheckPointer(buffer, NULL_OK));
639 PRECONDITION(CheckPointer(pdwLength));
640 } CONTRACTL_END;
641
642 if (g_dwSystemDirectory == 0)
643 SetInternalSystemDirectory();
644
645 //
646 // g_dwSystemDirectory includes the NULL in its count!
647 //
648 if(*pdwLength < g_dwSystemDirectory)
649 {
650 *pdwLength = g_dwSystemDirectory;
651 return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
652 }
653
654 if (buffer != NULL)
655 {
656 //
657 // wcsncpy_s will automatically append a null and g_dwSystemDirectory
658 // includes the null in its count, so we have to subtract 1.
659 //
660 wcsncpy_s(buffer, *pdwLength, g_pSystemDirectory, g_dwSystemDirectory-1);
661 }
662 *pdwLength = g_dwSystemDirectory;
663 return S_OK;
664}
665
666
667LPCWSTR GetInternalSystemDirectory(__out DWORD* pdwLength)
668{
669 LIMITED_METHOD_CONTRACT;
670
671 if (g_dwSystemDirectory == 0)
672 {
673 SetInternalSystemDirectory();
674 }
675
676 if (pdwLength != NULL)
677 {
678 * pdwLength = g_dwSystemDirectory;
679 }
680
681 return g_pSystemDirectory;
682}
683
684
685HRESULT SetInternalSystemDirectory()
686 {
687 CONTRACTL {
688 NOTHROW;
689 GC_NOTRIGGER;
690 } CONTRACTL_END;
691
692 HRESULT hr = S_OK;
693 if(g_dwSystemDirectory == 0) {
694
695 DWORD len = 0;
696 NewArrayHolder<WCHAR> pSystemDirectory;
697 EX_TRY{
698
699 // use local buffer for thread safety
700 PathString wzSystemDirectory;
701
702 hr = GetCORSystemDirectoryInternaL(wzSystemDirectory);
703
704 if (FAILED(hr)) {
705 wzSystemDirectory.Set(W('\0'));
706 }
707
708 pSystemDirectory = wzSystemDirectory.GetCopyOfUnicodeString();
709 if (pSystemDirectory == NULL)
710 {
711 hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
712 }
713 len = wzSystemDirectory.GetCount() + 1;
714
715 }
716 EX_CATCH_HRESULT(hr);
717
718 // publish results idempotently with correct memory ordering
719 g_pSystemDirectory = pSystemDirectory.Extract();
720
721 (void)InterlockedExchange((LONG *)&g_dwSystemDirectory, len);
722 }
723
724 return hr;
725}
726
727#if defined(CROSSGEN_COMPILE)
728void SetMscorlibPath(LPCWSTR wzSystemDirectory)
729{
730 DWORD len = (DWORD)wcslen(wzSystemDirectory);
731 bool appendSeparator = wzSystemDirectory[len-1] != DIRECTORY_SEPARATOR_CHAR_W;
732 DWORD lenAlloc = appendSeparator ? len+2 : len+1;
733 if (g_dwSystemDirectory < lenAlloc)
734 {
735 delete [] g_pSystemDirectory;
736 g_pSystemDirectory = new (nothrow) WCHAR[lenAlloc];
737
738 if (g_pSystemDirectory == NULL)
739 {
740 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
741 return;
742 }
743 }
744
745 wcscpy_s(g_pSystemDirectory, len+1, wzSystemDirectory);
746
747 if(appendSeparator)
748 {
749 g_pSystemDirectory[len] = DIRECTORY_SEPARATOR_CHAR_W;
750 g_pSystemDirectory[len+1] = W('\0');
751 g_dwSystemDirectory = len + 1;
752 }
753 else
754 {
755 g_dwSystemDirectory = len;
756 }
757}
758#endif
759