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