[Tizen] Unify dnetmemoryenumlib terms to match the codebase (#291)
[platform/upstream/coreclr.git] / src / utilcode / safewrap.cpp
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 // SafeWrap.cpp
6 //
7
8 //
9 // This file contains wrapper functions for Win32 API's that take SStrings 
10 // and use CLR-safe holders.
11 //
12 // See guidelines in SafeWrap.h for writing these APIs.
13 //*****************************************************************************
14
15 #include "stdafx.h"                     // Precompiled header key.
16 #include "safewrap.h"
17 #include "winwrap.h"                    // Header for macros and functions.
18 #include "utilcode.h"
19 #include "holder.h"
20 #include "sstring.h"
21 #include "ex.h"
22
23 //-----------------------------------------------------------------------------
24 // Get the current directory. 
25 // On success, returns true and sets 'Value' to unicode version of cur dir.
26 // Throws on all failures. This should mainly be oom.
27 //-----------------------------------------------------------------------------
28 void ClrGetCurrentDirectory(SString & value)
29 {
30     CONTRACTL
31     {
32         THROWS; 
33         GC_NOTRIGGER; 
34     }
35     CONTRACTL_END;
36
37     // Get size needed
38     DWORD len = WszGetCurrentDirectory(value);
39
40
41     // An actual API failure in GetCurrentDirectory failure should be very rare, so we'll throw on those.
42     if (len == 0)
43     {
44         ThrowLastError();
45     }
46 }
47
48 // Nothrowing wrapper.
49 bool ClrGetCurrentDirectoryNoThrow(SString & value)
50 {
51     CONTRACTL
52     {
53         NOTHROW; 
54         GC_NOTRIGGER;
55     }
56     CONTRACTL_END;
57     
58     bool fOk = true;
59     EX_TRY
60     {
61         ClrGetCurrentDirectory(value);
62     }
63     EX_CATCH
64     {
65         fOk = false;
66     }
67     EX_END_CATCH(SwallowAllExceptions)
68     return fOk;
69 }
70 //-----------------------------------------------------------------------------
71 // Reads an environment variable into the given SString.
72 // Returns true on success, false on failure (includes if the var does not exist).
73 // May throw on oom.
74 //-----------------------------------------------------------------------------
75 bool ClrGetEnvironmentVariable(LPCSTR szEnvVarName, SString & value)
76 {
77     CONTRACTL
78     {
79         THROWS; 
80         GC_NOTRIGGER;
81         
82         PRECONDITION(szEnvVarName != NULL);
83     }
84     CONTRACTL_END;
85
86     // First read it to get the needed length.
87     DWORD lenWithNull = GetEnvironmentVariableA(szEnvVarName, NULL, 0);
88     if (lenWithNull == 0)
89     {
90         return false;
91     }
92
93     // Now read it for content.
94     char * pCharBuf = value.OpenANSIBuffer(lenWithNull);                   
95     DWORD lenWithoutNull = GetEnvironmentVariableA(szEnvVarName, pCharBuf, lenWithNull);
96     value.CloseBuffer(lenWithoutNull);
97
98     if (lenWithoutNull != (lenWithNull - 1))
99     {
100         // Env var must have changed underneath us.
101         return false;
102     }
103     return true;
104 }
105
106 // Nothrowing wrapper.
107 bool ClrGetEnvironmentVariableNoThrow(LPCSTR szEnvVarName, SString & value)
108 {
109     CONTRACTL
110     {
111         NOTHROW; 
112         GC_NOTRIGGER;
113     }
114     CONTRACTL_END;
115     
116
117     bool fOk = false;
118     EX_TRY
119     {
120         fOk = ClrGetEnvironmentVariable(szEnvVarName, value);
121     }
122     EX_CATCH
123     {
124         fOk = false;
125     }
126     EX_END_CATCH(SwallowAllExceptions)
127     return fOk;
128 }
129
130 void ClrGetModuleFileName(HMODULE hModule, SString & value)
131 {
132     CONTRACTL
133     {
134         THROWS; 
135         GC_NOTRIGGER;
136     }
137     CONTRACTL_END;
138
139     WCHAR * pCharBuf = value.OpenUnicodeBuffer(_MAX_PATH);                   
140     DWORD numChars = GetModuleFileNameW(hModule, pCharBuf, _MAX_PATH);
141     value.CloseBuffer(numChars);
142 }
143
144 bool ClrGetModuleFileNameNoThrow(HMODULE hModule, SString & value)
145 {
146     CONTRACTL
147     {
148         NOTHROW; 
149         GC_NOTRIGGER;
150     }
151     CONTRACTL_END;
152
153     bool fOk = true;
154     EX_TRY
155     {
156         ClrGetModuleFileName(hModule, value);
157     }
158     EX_CATCH
159     {
160         fOk = false;
161     }
162     EX_END_CATCH(SwallowAllExceptions)
163     return fOk;
164 }
165
166 ClrDirectoryEnumerator::ClrDirectoryEnumerator(LPCWSTR pBaseDirectory, LPCWSTR pMask /*= W("*")*/)
167 {
168     CONTRACTL
169     {
170         THROWS; 
171         GC_NOTRIGGER;
172     }
173     CONTRACTL_END;
174
175     StackSString strMask(pBaseDirectory);
176     SString s(SString::Literal, DIRECTORY_SEPARATOR_STR_W);
177     if (!strMask.EndsWith(s))
178     {
179         strMask.Append(s);
180     }
181     strMask.Append(pMask);
182     dirHandle = WszFindFirstFile(strMask, &data);
183
184     if (dirHandle == INVALID_HANDLE_VALUE)
185     {
186         DWORD dwLastError = GetLastError();
187         
188         // We either ran out of files, or didnt encounter even a single file matching the 
189         // search mask. If it is neither of these conditions, then convert the error to an exception
190         // and raise it.
191         if ((dwLastError != ERROR_FILE_NOT_FOUND) && (dwLastError != ERROR_NO_MORE_FILES))
192             ThrowLastError();
193     }
194
195     fFindNext = FALSE;
196 }
197
198 bool ClrDirectoryEnumerator::Next()
199 {
200     CONTRACTL
201     {
202         THROWS; 
203         GC_NOTRIGGER;
204     }
205     CONTRACTL_END;
206
207     if (dirHandle == INVALID_HANDLE_VALUE)
208         return FALSE;
209
210     for (;;)
211     {
212         if (fFindNext)
213         {
214             if (!WszFindNextFile(dirHandle, &data))
215             {
216                 if (GetLastError() != ERROR_NO_MORE_FILES)
217                     ThrowLastError();
218
219                 return FALSE;
220             }
221         }
222         else
223         {
224             fFindNext  = TRUE;
225         }
226
227         // Skip junk
228         if (wcscmp(data.cFileName, W(".")) != 0 && wcscmp(data.cFileName, W("..")) != 0)
229             return TRUE;
230     }        
231 }
232
233 DWORD ClrReportEvent(
234     LPCWSTR     pEventSource,
235     WORD        wType,
236     WORD        wCategory,
237     DWORD       dwEventID,
238     PSID        lpUserSid,
239     WORD        wNumStrings,
240     LPCWSTR     *lpStrings,
241     DWORD       dwDataSize /*=0*/,
242     LPVOID      lpRawData /*=NULL*/)
243 {
244     CONTRACTL
245     {
246         NOTHROW; 
247         GC_NOTRIGGER;
248     }
249     CONTRACTL_END;
250
251 #ifndef FEATURE_PAL
252     HANDLE h = ::RegisterEventSourceW(
253         NULL, // uses local computer 
254         pEventSource);
255     if (h == NULL)
256     {
257         // Return the error code to the caller so that
258         // appropriate asserts/logging can be done
259         // incase of errors like event log being full
260         return GetLastError();
261     }
262
263     // Every event id should have matching description in dlls\shim\eventmsg.mc.  This allows
264     // event view to know how to display message.
265     _ASSERTE (dwEventID != 0);
266
267     // Attempt to report the event to the event log. Note that if the operation fails
268     // (for example because of low memory conditions) it isn't fatal so we can safely ignore 
269     // the return code from ReportEventW.
270     BOOL ret = ::ReportEventW(
271         h,                 // event log handle
272         wType,
273         wCategory,
274         dwEventID,
275         lpUserSid,
276         wNumStrings,
277         dwDataSize,
278         lpStrings,
279         lpRawData);
280
281     DWORD dwRetStatus = GetLastError();
282
283     ::DeregisterEventSource(h);
284
285     return (ret == TRUE)?ERROR_SUCCESS:dwRetStatus;
286 #else // FEATURE_PAL
287     // UNIXTODO: Report the event somewhere?
288     return ERROR_SUCCESS;
289 #endif // FEATURE_PAL
290 }
291
292 // Returns ERROR_SUCCESS if succeessful in reporting to event log, or 
293 // Windows error code to indicate the specific error.
294 DWORD ClrReportEvent(
295     LPCWSTR     pEventSource,
296     WORD        wType,
297     WORD        wCategory,
298     DWORD       dwEventID,
299     PSID        lpUserSid,
300     LPCWSTR     pMessage)
301 {
302     return ClrReportEvent(pEventSource, wType, wCategory, dwEventID, lpUserSid, 1, &pMessage);
303 }
304
305 #ifndef FEATURE_PAL
306 // Read a REG_SZ (null-terminated string) value from the registry.  Throws.
307 // 
308 // Arguments:
309 //     hKey - key to registry hive.
310 //     szValueName - null-terminated string for value name to lookup. 
311 //        If this is empty, this gets the (default) value in the registry hive.
312 //     value - out parameter to hold registry value string contents. 
313 //     
314 // Returns:
315 //    value is set on success. Throws on any failure, including if the value doesn't exist 
316 //    or if the value exists but is not a REG_SZ.
317 //    
318 // Notes:
319 //    REG_SZ is a single null-terminated string in the registry.
320 //    This is only support on Windows because the registry is windows specific.
321 void ClrRegReadString(HKEY hKey, const SString & szValueName, SString & value)
322 {
323     CONTRACTL
324     {
325         THROWS; 
326         GC_NOTRIGGER; 
327     }
328     CONTRACTL_END;
329
330     DWORD type;    
331     DWORD numBytesData;
332
333     // Preemptively clear the string such that it's empty in any failure case.
334     value.Clear();
335
336     //
337     // Step 1:  First call to find size of buffer and ensure data type is correct
338     // 
339     LONG ret = WszRegQueryValueEx(
340       hKey,
341       szValueName.GetUnicode(), // NULL or "\0" represents the (default) key.
342       NULL, // reserved
343       &type, // should be REG_SZ
344       NULL, // not requesting data yet
345       &numBytesData
346     );
347
348     if (ret != ERROR_SUCCESS)
349     {
350         ThrowWin32(ret);
351     }
352
353     if (type != REG_SZ)
354     {
355         // The registry value is not a string.
356         ThrowHR(E_INVALIDARG);
357     }
358
359     // REG_SZ includes the null terminator. 
360     DWORD numCharsIncludingNull = numBytesData / sizeof(WCHAR);
361
362     // 
363     //  Step 2: Allocate buffer to hold final result
364     // 
365     WCHAR * pData = value.OpenUnicodeBuffer(numCharsIncludingNull);
366     DWORD numBytesData2 = numBytesData;
367
368
369     //
370     // Step 3: Requery to get actual contents
371     // 
372     ret = WszRegQueryValueEx(
373       hKey,
374       szValueName.GetUnicode(),
375       NULL, // reserved
376       &type, // should still be REG_SZ
377       (LPBYTE) pData, 
378       &numBytesData2
379     );
380
381     // This check should only fail if the registry was changed inbetween the first query
382     // and the second. In practice, that should never actually happen.
383     if ((numBytesData2 != numBytesData) || (type != REG_SZ))
384     {
385         // On error, leave string empty.
386         value.CloseBuffer(0);
387
388         ThrowHR(E_FAIL);
389     }
390
391     if (ret != ERROR_SUCCESS)
392     {
393         // On error, leave string empty.
394         value.CloseBuffer(0);
395         ThrowWin32(ret);
396     }
397
398     
399     //
400     // Step 4:  Close the string buffer
401     // 
402     COUNT_T numCharsNoNull = numCharsIncludingNull - 1;
403     value.CloseBuffer(numCharsNoNull);
404 }
405 #endif // FEATURE_PAL