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.h |
6 | // |
7 | |
8 | // |
9 | // This file contains wrapper functions for Win32 API's that take SStrings |
10 | // and use CLR-safe holders. |
11 | //***************************************************************************** |
12 | |
13 | |
14 | /* |
15 | Guidelines for SafeWrapper APIs: |
16 | Most of these are 'common-sense', plus a few arbitrary decisions thrown in for |
17 | consistency's sake. |
18 | |
19 | - THROWING: Throw on oom, but return all other failure codes. |
20 | The rationale here is that SString operations already throw, so to make these APIs |
21 | non-throwing would require an extra EX_TRY/EX_CATCH. Most callees will want to throw |
22 | on OOM anyways. So making these APIs non-throwing would mean an extra try/catch in |
23 | the caller + an extra check at the callee. We can eliminate that overhead and just make |
24 | it throwing. |
25 | |
26 | Return non-oom failure codes because callees actually freqeuntly expect an API to fail. |
27 | For example, the callee will have special handling for file-not-found. |
28 | |
29 | For convenience, you could add a no-throwing wrapper version of the API: |
30 | ClrGetEnvironmentVariable <-- default throws on oom. |
31 | ClrGetEnvironmentVariableNoThrow <-- never throws. |
32 | |
33 | - NAMING: Prefix the name with 'Clr', just like we do for win32 APIs going through hosting. |
34 | |
35 | - DON'T FORGET CONTRACTS: Most of these APIs will likely be Throws/GC_Notrigger. |
36 | Also use PRECONDITIONs + POSTCONDITIONS when possible. |
37 | |
38 | - SIGNATURES: Keep the method signture as close the the original win32 API as possible. |
39 | - Preserve the return type + value. (except allow it to throw on oom). If the return value |
40 | should be a holder, then use that as an out-parameter at the end of the argument list. |
41 | We don't want to return holders because that will cause the dtors to be called. |
42 | - For input strings use 'const SString &' instead of 'LPCWSTR'. |
43 | - Change ('out' string, length) pairs to 'SString &' (this is the convention the rest of the CLR uses for SStrings) |
44 | - Use Holders where appropriate. |
45 | - Preserve other parameters. |
46 | |
47 | - USE SSTRINGS TO AVOID BUFFER OVERRUN ISSUES: Repeated here for emphasis. Use SStrings when |
48 | applicable to make it very easy to verify the code does not have buffer overruns. |
49 | This will also simplify callsites from having to figure out the length of the output string. |
50 | |
51 | - USE YOUR BEST JUDGEMENT: The primary goal of these API wrappers is to embrace 'security-safe' practices. |
52 | Certainly take any additional steps to that goal. For example, it may make sense to eliminate |
53 | corner case inputs for a given API or to break a single confusing API up into several discrete and |
54 | move obvious APIs. |
55 | |
56 | */ |
57 | #ifndef _safewrap_h_ |
58 | #define _safewrap_h_ |
59 | |
60 | #include "holder.h" |
61 | |
62 | class SString; |
63 | bool ClrGetEnvironmentVariable(LPCSTR szEnvVarName, SString & value); |
64 | bool ClrGetEnvironmentVariableNoThrow(LPCSTR szEnvVarName, SString & value); |
65 | void ClrGetModuleFileName(HMODULE hModule, SString & value); |
66 | bool ClrGetModuleFileNameNoThrow(HMODULE hModule, SString & value); |
67 | |
68 | void ClrGetCurrentDirectory(SString & value); |
69 | bool ClrGetCurrentDirectoryNoThrow(SString & value); |
70 | |
71 | |
72 | /* --------------------------------------------------------------------------- * |
73 | * Simple wrapper around WszFindFirstFile/WszFindNextFile |
74 | * --------------------------------------------------------------------------- */ |
75 | class ClrDirectoryEnumerator |
76 | { |
77 | WIN32_FIND_DATAW data; |
78 | FindHandleHolder dirHandle; |
79 | BOOL fFindNext; // Skip FindNextFile first time around |
80 | |
81 | public: |
82 | ClrDirectoryEnumerator(LPCWSTR pBaseDirectory, LPCWSTR pMask = W("*" )); |
83 | bool Next(); |
84 | |
85 | LPCWSTR GetFileName() |
86 | { |
87 | return data.cFileName; |
88 | } |
89 | |
90 | DWORD GetFileAttributes() |
91 | { |
92 | return data.dwFileAttributes; |
93 | } |
94 | |
95 | void Close() |
96 | { |
97 | dirHandle.Clear(); |
98 | } |
99 | }; |
100 | |
101 | // Read a REG_SZ (null-terminated string) value from the registry. Throws. |
102 | void ClrRegReadString(HKEY hKey, const SString & szValueName, SString & value); |
103 | |
104 | /* --------------------------------------------------------------------------- * |
105 | * Simple wrapper around RegisterEventSource/ReportEvent/DeregisterEventSource |
106 | * --------------------------------------------------------------------------- */ |
107 | // Returns ERROR_SUCCESS if succeessful in reporting to event log, or |
108 | // Windows error code to indicate the specific error. |
109 | DWORD ClrReportEvent( |
110 | LPCWSTR pEventSource, |
111 | WORD wType, |
112 | WORD wCategory, |
113 | DWORD dwEventID, |
114 | PSID lpUserSid, |
115 | WORD wNumStrings, |
116 | LPCWSTR *lpStrings, |
117 | DWORD dwDataSize = 0, |
118 | LPVOID lpRawData = NULL); |
119 | |
120 | DWORD ClrReportEvent( |
121 | LPCWSTR pEventSource, |
122 | WORD wType, |
123 | WORD wCategory, |
124 | DWORD dwEventID, |
125 | PSID lpUserSid, |
126 | LPCWSTR pMessage); |
127 | |
128 | //***************************************************************************** |
129 | // This provides a wrapper around GetFileSize() that forces it to fail |
130 | // if the file is >4g and pdwHigh is NULL. Other than that, it acts like |
131 | // the genuine GetFileSize(). |
132 | // |
133 | // |
134 | //***************************************************************************** |
135 | DWORD inline SafeGetFileSize(HANDLE hFile, DWORD *pdwHigh) |
136 | { |
137 | if (pdwHigh != NULL) |
138 | { |
139 | return ::GetFileSize(hFile, pdwHigh); |
140 | } |
141 | else |
142 | { |
143 | DWORD hi; |
144 | DWORD lo = ::GetFileSize(hFile, &hi); |
145 | if (lo == 0xffffffff && GetLastError() != NO_ERROR) |
146 | { |
147 | return lo; |
148 | } |
149 | // api succeeded. is the file too large? |
150 | if (hi != 0) |
151 | { |
152 | // there isn't really a good error to set here... |
153 | SetLastError(ERROR_NOT_ENOUGH_MEMORY); |
154 | return 0xffffffff; |
155 | } |
156 | |
157 | if (lo == 0xffffffff) |
158 | { |
159 | // note that a success return of (hi=0,lo=0xffffffff) will be |
160 | // treated as an error by the caller. Again, that's part of the |
161 | // price of being a slacker and not handling the high dword. |
162 | // We'll set a lasterror for him to pick up. |
163 | SetLastError(ERROR_NOT_ENOUGH_MEMORY); |
164 | } |
165 | |
166 | return lo; |
167 | } |
168 | |
169 | } |
170 | |
171 | #endif // _safewrap_h_ |
172 | |