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 |
406 | |